Ignore:
Timestamp:
02/14/13 15:20:07 (11 years ago)
Author:
pharms
Message:
  • support of new HTML logging format
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/autoquest-plugin-html/src/main/java/de/ugoe/cs/autoquest/plugin/html/HTMLLogParser.java

    r1064 r1069  
    1515package de.ugoe.cs.autoquest.plugin.html; 
    1616 
    17 import java.io.File; 
    18 import java.io.FileInputStream; 
    19 import java.io.FileNotFoundException; 
    20 import java.io.IOException; 
    21 import java.io.InputStreamReader; 
    22 import java.io.UnsupportedEncodingException; 
    23 import java.util.Collection; 
    24 import java.util.HashMap; 
    25 import java.util.LinkedList; 
    2617import java.util.List; 
    2718import java.util.Map; 
    28  
    29 import javax.xml.parsers.ParserConfigurationException; 
    30 import javax.xml.parsers.SAXParser; 
    31 import javax.xml.parsers.SAXParserFactory; 
    32  
    33 import org.xml.sax.Attributes; 
    34 import org.xml.sax.InputSource; 
     19import java.util.regex.Matcher; 
     20import java.util.regex.Pattern; 
     21 
    3522import org.xml.sax.SAXException; 
    36 import org.xml.sax.SAXParseException; 
    37 import org.xml.sax.helpers.DefaultHandler; 
    3823 
    3924import de.ugoe.cs.autoquest.eventcore.Event; 
    4025import de.ugoe.cs.autoquest.eventcore.IEventType; 
    41 import de.ugoe.cs.autoquest.eventcore.guimodel.GUIElementTree; 
    4226import de.ugoe.cs.autoquest.eventcore.guimodel.GUIModel; 
    4327import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement; 
    4428import de.ugoe.cs.autoquest.plugin.html.eventcore.HTMLEventTypeFactory; 
     29import de.ugoe.cs.autoquest.plugin.html.guimodel.HTMLDocumentSpec; 
    4530import de.ugoe.cs.autoquest.plugin.html.guimodel.HTMLGUIElement; 
    4631import de.ugoe.cs.autoquest.plugin.html.guimodel.HTMLGUIElementSpec; 
    4732import de.ugoe.cs.autoquest.plugin.html.guimodel.HTMLPageElementSpec; 
    48 import de.ugoe.cs.autoquest.plugin.html.guimodel.HTMLPageSpec; 
    4933import de.ugoe.cs.autoquest.plugin.html.guimodel.HTMLServerSpec; 
    50 import de.ugoe.cs.util.console.Console; 
    5134 
    5235/** 
    5336 * <p> 
    5437 * This class provides the functionality to parse XML log files generated by the HTMLMonitor of 
    55  * autoquest. The result of parsing a file is a collection of event sequences. 
     38 * AutoQUEST. The result of parsing a file is a collection of event sequences and a GUI model 
    5639 * </p> 
    5740 *  
    58  * @author Fabian Glaser 
     41 * @author Fabian Glaser, Patrick Harms 
    5942 * @version 1.0 
    6043 *  
    6144 */ 
    62 public class HTMLLogParser extends DefaultHandler { 
     45public class HTMLLogParser extends AbstractDefaultLogParser { 
     46     
     47    /** 
     48     * 
     49     */ 
     50    private Pattern htmlElementPattern = 
     51        Pattern.compile("(\\w+)(\\[(\\d+)\\]|\\(htmlId=([\\w-]+)\\))"); 
     52 
     53    /* (non-Javadoc) 
     54     * @see de.ugoe.cs.autoquest.plugin.html.AbstractDefaultLogParser#handleGUIElement(java.lang.String, java.util.Map) 
     55     */ 
     56    @Override 
     57    protected boolean handleGUIElement(String id, Map<String, String> parameters) 
     58        throws SAXException 
     59    { 
     60        HTMLGUIElementSpec specification = null; 
     61         
     62        String parentId = parameters.get("parent"); 
     63        IGUIElement parent = super.getGUIElementTree().find(parentId); 
     64 
     65        if (parameters.containsKey("host")) { 
     66            // this is a server specification 
     67            int port = 80; 
     68            String portStr = parameters.get(port); 
     69             
     70            if (portStr != null) { 
     71                port = Integer.parseInt(portStr); 
     72            } 
     73             
     74            specification = new HTMLServerSpec(parameters.get("host"), port); 
     75        } 
     76        else if (parameters.containsKey("path")) { 
     77            // this is a document specification 
     78             
     79            if (parent != null) { 
     80                if (!(parent.getSpecification() instanceof HTMLServerSpec)) { 
     81                    throw new SAXException 
     82                        ("invalid log: parent GUI element of a document is not of type server"); 
     83                } 
     84                 
     85                specification = new HTMLDocumentSpec 
     86                    ((HTMLServerSpec) parent.getSpecification(), parameters.get("path"), 
     87                     parameters.get("query"), parameters.get("title")); 
     88            } 
     89            else if (parentId == null) { 
     90                throw new SAXException("invalid log: a document has no parent id"); 
     91            } 
     92        } 
     93        else if (parameters.containsKey("tagname")) { 
     94            String tagName = parameters.get("tagname"); 
     95             
     96            if (!tagNameMustBeConsidered(tagName)) { 
     97                return true; 
     98            } 
     99 
     100            if (parent != null) { 
     101                IGUIElement document = parent; 
     102                 
     103                while ((document != null) && 
     104                       (!(document.getSpecification() instanceof HTMLDocumentSpec))) 
     105                { 
     106                    document = document.getParent(); 
     107                } 
     108                 
     109                if (document == null) { 
     110                    throw new SAXException 
     111                        ("invalid log: parent hierarchy of a page element does not contain a " + 
     112                         "document"); 
     113                } 
     114                 
     115                int index = -1; 
     116                String indexStr = parameters.get("index"); 
     117 
     118                if ((indexStr != null) && (!"".equals(indexStr))) { 
     119                    index = Integer.parseInt(indexStr); 
     120                } 
     121 
     122                specification = new HTMLPageElementSpec 
     123                    ((HTMLDocumentSpec) document.getSpecification(), tagName, 
     124                     parameters.get("htmlid"), index); 
     125            } 
     126            else if (parentId == null) { 
     127                throw new SAXException("invalid log: a page element has no parent id"); 
     128            } 
     129        } 
     130        else { 
     131            throw new SAXException("invalid log: unknown GUI element"); 
     132        } 
     133 
     134        if (specification != null) { 
     135            super.getGUIElementTree().add(id, parentId, specification); 
     136            return true; 
     137        } 
     138        else { 
     139            return false; 
     140        } 
     141    } 
     142 
     143    /* (non-Javadoc) 
     144     * @see de.ugoe.cs.autoquest.plugin.html.AbstractDefaultLogParser#handleEvent(java.lang.String, java.util.Map) 
     145     */ 
     146    @Override 
     147    protected boolean handleEvent(String type, Map<String, String> parameters) throws SAXException { 
     148        String targetId = parameters.get("target"); 
     149         
     150        if (targetId == null) { 
     151            String targetDocument = parameters.get("targetDocument"); 
     152            String targetDOMPath = parameters.get("targetDOMPath"); 
     153             
     154            if ((targetDocument == null) || (targetDOMPath == null)) { 
     155                throw new SAXException("event has no target defined"); 
     156            } 
     157             
     158            targetId = determineTargetId(targetDocument, targetDOMPath); 
     159             
     160            if (targetId == null) { 
     161                // the target id can not be determined yet 
     162                return false; 
     163            } 
     164        } 
     165         
     166        IGUIElement target = super.getGUIElementTree().find(targetId); 
     167         
     168        if (target == null) { 
     169            // event not processable yet 
     170            return false; 
     171        } 
     172 
     173        IEventType eventType = 
     174            HTMLEventTypeFactory.getInstance().getEventType(type, parameters, target); 
     175         
     176        Event event = new Event(eventType, target); 
     177 
     178        String timestampStr = parameters.get("timestamp"); 
     179         
     180        if (timestampStr != null) { 
     181            event.setTimestamp(Long.parseLong(timestampStr)); 
     182        } 
     183 
     184        ((HTMLGUIElement) event.getTarget()).markUsed(); 
     185         
     186        super.addToSequence(event); 
     187 
     188        return true; 
     189    } 
     190 
    63191    /** 
    64192     * <p> 
    65      * Constructor. Creates a new HTMLLogParser. 
     193     * TODO: comment 
    66194     * </p> 
    67      */ 
    68     public HTMLLogParser() { 
    69         sequences = new LinkedList<List<Event>>(); 
     195     * 
     196     * @param targetDocument 
     197     * @param targetDOMPath 
     198     * @return 
     199     */ 
     200    private String determineTargetId(String targetDocument, String targetDOMPath) 
     201        throws SAXException 
     202    { 
     203        IGUIElement document = super.getGUIElementTree().find(targetDocument); 
     204         
     205        if (document == null) { 
     206            return null; 
     207        } 
     208         
     209        if (!(document.getSpecification() instanceof HTMLDocumentSpec)) { 
     210            throw new SAXException("an id that should refer to an HTML document refers to" + 
     211                                   "something else"); 
     212        } 
     213         
     214        GUIModel model = super.getGUIElementTree().getGUIModel(); 
     215        IGUIElement child = document; 
     216        String[] pathElements = targetDOMPath.split("/"); 
     217        int pathIndex = 0; 
     218         
     219        HTMLPageElementSpec compareSpec; 
     220        String tagName; 
     221        int index; 
     222        String htmlId; 
     223         
     224        while ((pathIndex < pathElements.length) && (child != null)) { 
     225            if ((pathElements[pathIndex] != null) && (!"".equals(pathElements[pathIndex]))) {            
     226                Matcher matcher = htmlElementPattern.matcher(pathElements[pathIndex]); 
     227                if (!matcher.matches()) { 
     228                    throw new SAXException 
     229                        ("could not parse target DOM path element " + pathElements[pathIndex]); 
     230                } 
     231 
     232                tagName = matcher.group(1); 
     233                String indexStr = matcher.group(3); 
     234                htmlId = matcher.group(4); 
     235 
     236                index = -1; 
     237                if ((indexStr != null) && (!"".equals(indexStr))) { 
     238                    index = Integer.parseInt(indexStr); 
     239                } 
     240 
     241                compareSpec = new HTMLPageElementSpec 
     242                    ((HTMLDocumentSpec) document.getSpecification(), tagName, htmlId, index); 
     243 
     244                List<IGUIElement> children = model.getChildren(child); 
     245                child = null; 
     246 
     247                for (IGUIElement candidate : children) { 
     248                    if (compareSpec.getSimilarity(candidate.getSpecification())) { 
     249                        child = candidate; 
     250                        break; 
     251                    } 
     252                } 
     253            } 
     254             
     255            pathIndex++; 
     256        } 
     257         
     258        if (child != null) { 
     259            return super.getGUIElementTree().find(child); 
     260        } 
     261        else { 
     262            return null; 
     263        } 
    70264    } 
    71265 
    72266    /** 
    73267     * <p> 
    74      * Collection of event sequences that is contained in the parsed log file. 
     268     * checks if tags with the provided name must be handled in the GUI model. As an example, 
     269     * it is not necessary to handle "head" tags and anything included in them.  
    75270     * </p> 
    76      */ 
    77     private Collection<List<Event>> sequences; 
    78  
    79     /** 
    80      * <p> 
    81      * Internal handle to the parsed GUI structure, stored in a GUIElementTree 
    82      * </p> 
    83      */ 
    84     private GUIElementTree<String> currentGUIElementTree; 
    85  
    86     /** 
    87      * <p> 
    88      * Path of the GUI element currently being parsed. 
    89      * </p> 
    90      */ 
    91     private String currentGUIElementPath; 
    92  
    93     /** 
    94      * <p> 
    95      * Path of the parent of the GUI element currently being parsed. 
    96      * </p> 
    97      */ 
    98     private String currentParentPath; 
    99  
    100     /** 
    101      * <p> 
    102      * Source of the GUI element currently being parsed. 
    103      * </p> 
    104      */ 
    105     private String currentEventSource; 
    106  
    107     /** 
    108      * <p> 
    109      * Timestamp of the event currently being parsed. 
    110      * </p> 
    111      */ 
    112     private Long currentEventTimestamp; 
    113  
    114     /** 
    115      * <p> 
    116      * Internal handle to the parameters of the event currently being parsed. 
    117      * </p> 
    118      */ 
    119     private Map<String, String> currentEventParameters; 
    120  
    121     /** 
    122      * <p> 
    123      * Internal handle to the parameters of the GUI element currently being parsed. 
    124      * </p> 
    125      */ 
    126     private Map<String, String> currentGUIElementParameters; 
    127     /** 
    128      * <p> 
    129      * Internal handle to the sequence currently being parsed. 
    130      * </p> 
    131      */ 
    132     private List<Event> currentSequence; 
    133  
    134     /** 
    135      * <p> 
    136      * Internal handle to type of the event currently being parsed. 
    137      * </p> 
    138      */ 
    139     private String currentEventType; 
    140  
    141     /** 
    142      * <p> 
    143      * Class of the GUI element currently being parsed. 
    144      * </p> 
    145      */ 
    146     private String currentGUIElementClass; 
    147  
    148     /** 
    149      * <p> 
    150      * Index of the GUI element currently being parsed. 
    151      * </p> 
    152      */ 
    153     private String currentGUIElementIndex; 
    154  
    155     /** 
    156      * <p> 
    157      * internal handle to the GUI element of the previous event to be potentially reused for the 
    158      * current 
    159      * </p> 
    160      */ 
    161     private IGUIElement lastGUIElement; 
    162  
    163     /** 
    164      * <p> 
    165      * internal handle to the server specification currently being used. 
    166      * </p> 
    167      */ 
    168     private HTMLServerSpec currentServerSpec; 
    169  
    170     /** 
    171      * <p> 
    172      * Parses a log file written by the HTMLMonitor and creates a collection of event sequences. 
    173      * </p> 
    174      *  
    175      * @param filename 
    176      *            name and path of the log file 
    177      */ 
    178     public void parseFile(String filename) { 
    179         if (filename == null) { 
    180             throw new IllegalArgumentException("filename must not be null"); 
    181         } 
    182  
    183         parseFile(new File(filename)); 
    184     } 
    185  
    186     /** 
    187      * <p> 
    188      * Parses a log file written by the HTMLMonitor and creates a collection of event sequences. 
    189      * </p> 
    190      *  
    191      * @param file 
    192      *            file to be parsed 
    193      */ 
    194     public void parseFile(File file) { 
    195         if (file == null) { 
    196             throw new IllegalArgumentException("file must not be null"); 
    197         } 
    198         SAXParserFactory spf = SAXParserFactory.newInstance(); 
    199         spf.setValidating(true); 
    200         SAXParser saxParser = null; 
    201         InputSource inputSource = null; 
    202         try { 
    203             saxParser = spf.newSAXParser(); 
    204             inputSource = 
    205                 new InputSource(new InputStreamReader(new FileInputStream(file), "UTF-8")); 
    206         } 
    207         catch (UnsupportedEncodingException e) { 
    208             Console.printerr("Error parsing file " + file.getName()); 
    209             Console.logException(e); 
    210             return; 
    211         } 
    212         catch (ParserConfigurationException e) { 
    213             Console.printerr("Error parsing file " + file.getName()); 
    214             Console.logException(e); 
    215             return; 
    216         } 
    217         catch (SAXException e) { 
    218             Console.printerr("Error parsing file " + file.getName()); 
    219             Console.logException(e); 
    220         } 
    221         catch (FileNotFoundException e) { 
    222             Console.printerr("Error parsing file " + file.getName()); 
    223             Console.logException(e); 
    224         } 
    225         if (inputSource != null) { 
    226             inputSource.setSystemId("file://" + file.getAbsolutePath()); 
    227             try { 
    228                 if (saxParser == null) { 
    229                     throw new RuntimeException("SaxParser creation failed"); 
    230                 } 
    231                 saxParser.parse(inputSource, this); 
    232             } 
    233             catch (SAXParseException e) { 
    234                 Console.printerrln("Failure parsing file in line " + e.getLineNumber() + 
    235                     ", column " + e.getColumnNumber() + "."); 
    236                 Console.logException(e); 
    237             } 
    238             catch (SAXException e) { 
    239                 Console.printerr("Error parsing file " + file.getName()); 
    240                 Console.logException(e); 
    241                 return; 
    242             } 
    243             catch (IOException e) { 
    244                 Console.printerr("Error parsing file " + file.getName()); 
    245                 Console.logException(e); 
    246                 return; 
    247             } 
    248         } 
    249     } 
    250  
    251     /* 
    252      * (non-Javadoc) 
    253      *  
    254      * @see org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String, java.lang.String, 
    255      * java.lang.String, org.xml.sax.Attributes) 
    256      */ 
    257     @Override 
    258     public void startElement(String uri, String localName, String qName, Attributes atts) 
    259         throws SAXException 
    260     { 
    261         if (qName.equals("session")) { 
    262             currentSequence = new LinkedList<Event>(); 
    263             if (currentGUIElementTree == null) 
    264                 currentGUIElementTree = new GUIElementTree<String>(); 
    265         } 
    266         else if (qName.equals("component")) { 
    267             currentGUIElementPath = atts.getValue("path"); 
    268             currentGUIElementParameters = new HashMap<String, String>(); 
    269         } 
    270         else if (qName.equals("event")) { 
    271             currentEventType = atts.getValue("type"); 
    272             currentEventParameters = new HashMap<String, String>(); 
    273         } 
    274         else if (qName.equals("param")) { 
    275             String paramName = atts.getValue("name"); 
    276             if (currentGUIElementPath != null) { 
    277                 if ("parent".equals(paramName)) { 
    278                     currentParentPath = atts.getValue("value"); 
    279                 } 
    280                 if ("class".equals(paramName)) { 
    281                     currentGUIElementClass = atts.getValue("value"); 
    282                 } 
    283                 if ("index".equals(paramName)) { 
    284                     currentGUIElementIndex = atts.getValue("value"); 
    285                 } 
    286                 currentGUIElementParameters.put(paramName, atts.getValue("value")); 
    287             } 
    288             else if (currentEventType != null) { 
    289                 if ("target".equals(paramName)) { 
    290                     currentEventSource = atts.getValue("value"); 
    291                 } 
    292                 if ("timestamp".equals(paramName)) { 
    293                     currentEventTimestamp = Long.parseLong(atts.getValue("value")); 
    294                 } 
    295                 currentEventParameters.put(paramName, atts.getValue("value")); 
    296             } 
    297             else { 
    298                 throw new SAXException("param tag found where it should not be."); 
    299             } 
    300         } 
    301         else { 
    302             throw new SAXException("unknown tag found: " + qName); 
    303         } 
    304  
    305     } 
    306  
    307     /* 
    308      * (non-Javadoc) 
    309      *  
    310      * @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String, java.lang.String, 
    311      * java.lang.String) 
    312      */ 
    313     @Override 
    314     public void endElement(String uri, String localName, String qName) throws SAXException { 
    315         if (qName.equals("session")) { 
    316             if (currentSequence != null && !currentSequence.isEmpty()) { 
    317                 sequences.add(currentSequence); 
    318             } 
    319             currentSequence = null; 
    320         } 
    321         else if (qName.equals("component") && currentGUIElementPath != null) { 
    322             HTMLGUIElementSpec guiElementSpec = 
    323                 getGUIElementSpec(currentGUIElementClass, currentGUIElementParameters); 
    324             currentGUIElementTree.add(currentGUIElementPath, currentParentPath, guiElementSpec); 
    325  
    326             currentParentPath = null; 
    327             currentGUIElementPath = null; 
    328             currentGUIElementParameters = null; 
    329         } 
    330         else if (qName.equals("event")) { 
    331             IGUIElement currentGUIElement; 
    332             currentGUIElement = currentGUIElementTree.find(currentEventSource); 
    333  
    334             IEventType eventType = 
    335                 HTMLEventTypeFactory.getInstance().getEventType(currentEventType, 
    336                                                                 currentEventParameters, 
    337                                                                 currentGUIElement); 
    338             Event event = 
    339                 new Event(eventType, (currentGUIElement == null ? lastGUIElement 
    340                     : currentGUIElement)); 
    341  
    342             event.setTimestamp(currentEventTimestamp); 
    343             HTMLGUIElement currentEventTarget = (HTMLGUIElement) event.getTarget(); 
    344             currentEventTarget.markUsed(); 
    345             currentSequence.add(event); 
    346  
    347             currentEventSource = null; 
    348             currentEventTimestamp = -1l; 
    349             currentEventParameters = null; 
    350             currentEventType = null; 
    351  
    352             if (currentGUIElement != null) { 
    353                 lastGUIElement = currentGUIElement; 
    354             } 
    355  
    356             currentGUIElement = null; 
    357         } 
    358     } 
    359  
    360     /** 
    361      * <p> 
    362      * Returns a collection of event sequences that was obtained from parsing log files. 
    363      * </p> 
    364      *  
     271     * 
     272     * @param tagName 
    365273     * @return 
    366274     */ 
    367     public Collection<List<Event>> getSequences() { 
    368         return sequences; 
    369     } 
    370  
    371     /** 
    372      * <p> 
    373      * Returns the GUI model that is obtained from parsing log files. 
    374      * </p> 
    375      *  
    376      * @return GUIModel 
    377      */ 
    378     public GUIModel getGuiModel() { 
    379         return currentGUIElementTree.getGUIModel(); 
    380     } 
    381  
    382     /** 
    383      * Returns the HTMLGUIElementSpecification for a GUI Element described 
    384      * by its class name and its parameters. 
    385      * @param guiElementClass 
    386      * @param guiElementParameters 
    387      * @return 
    388      */ 
    389     private HTMLGUIElementSpec getGUIElementSpec(String guiElementClass, 
    390                                                  Map<String, String> guiElementParameters) 
    391     { 
    392         HTMLGUIElementSpec specification = null; 
    393         if ("server".equals(guiElementClass)) { 
    394             // TODO: add correct port handling 
    395             specification = new HTMLServerSpec(guiElementParameters.get("htmlId"), 0); 
    396             currentServerSpec = (HTMLServerSpec) specification; 
    397         } 
    398  
    399         else { 
    400             String id = guiElementParameters.get("htmlId"); 
    401             if (id == null) { 
    402                 HTMLPageElementSpec parentSpec = 
    403                     (HTMLPageElementSpec) currentGUIElementTree.find(currentParentPath) 
    404                         .getSpecification(); 
    405                 id = parentSpec.getPage().getPagePath(); 
    406             } 
    407  
    408             int index = -1; 
    409             String indexStr = guiElementParameters.get("index"); 
    410  
    411             if ((indexStr != null) && (!"".equals(indexStr))) { 
    412                 index = Integer.parseInt(indexStr); 
    413             } 
    414             String title = guiElementParameters.get("title"); 
    415             HTMLPageSpec page = new HTMLPageSpec(currentServerSpec, id, title); 
    416             specification = new HTMLPageElementSpec(page, guiElementClass, id, index); 
    417         } 
    418  
    419         return specification; 
    420     } 
     275    private boolean tagNameMustBeConsidered(String tagName) { 
     276        return 
     277            !"head".equals(tagName) && !"title".equals(tagName) && !"script".equals(tagName) && 
     278            !"style".equals(tagName) && !"link".equals(tagName) && !"meta".equals(tagName) && 
     279            !"iframe".equals(tagName) && !"input_hidden".equals(tagName) && 
     280            !"option".equals(tagName) && !"tt".equals(tagName) && !"br".equals(tagName) && 
     281            !"colgroup".equals(tagName) && !"col".equals(tagName); 
     282 
     283    } 
     284 
    421285} 
Note: See TracChangeset for help on using the changeset viewer.