Ignore:
Timestamp:
04/11/14 12:12:52 (10 years ago)
Author:
pharms
Message:
  • adapted parsing of HTML files to have more power in specifying replacements
Location:
trunk/autoquest-plugin-html/src/main
Files:
7 edited

Legend:

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

    r1435 r1496  
    1919import java.io.FileNotFoundException; 
    2020import java.io.IOException; 
    21 import java.util.Arrays; 
    2221import java.util.HashMap; 
     22import java.util.LinkedList; 
    2323import java.util.List; 
    2424import java.util.Map; 
     
    3535import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement; 
    3636import de.ugoe.cs.autoquest.plugin.html.eventcore.HTMLEventTypeFactory; 
     37import de.ugoe.cs.autoquest.plugin.html.guimodel.HTMLDocument; 
    3738import de.ugoe.cs.autoquest.plugin.html.guimodel.HTMLDocumentSpec; 
    3839import de.ugoe.cs.autoquest.plugin.html.guimodel.HTMLGUIElement; 
     
    4849 * </p> 
    4950 * <p> 
    50  * The parser can be configured with parsing parameters to ignore, e.g., ids or indexes of 
     51 * The parser can be configured with parsing parameters to adapt, e.g., ids or or ignore indexes of 
    5152 * parsed GUI elements. Details can be found in the manual pages of the respective parsing commands. 
    5253 * </p> 
     
    6869    /** 
    6970     * <p> 
    70      * the pattern used for parsing parsing parameters 
    71      * </p> 
    72      */ 
    73     private Pattern htmlElementSpecPattern = 
    74         Pattern.compile("(\\w+)(\\[(\\d+)\\]|\\(htmlId=([\\w-#]+)\\))?"); 
    75      
    76     /** 
    77      * <p> 
    78      * parameters to influence parsing 
    79      * </p> 
    80      */ 
    81     private Map<String, List<String>> parseParams; 
     71     * file containing parameters to influence parsing 
     72     * </p> 
     73     */ 
     74    private String parseParamFile; 
    8275 
    8376    /** 
     
    8679     * </p> 
    8780     */ 
    88     private Map<String, String> idReplacements; 
    89  
    90     /** 
    91      * <p> 
    92      * initializes the parser with the parsing parameters to be considered 
     81    private Map<String, List<ReplacementSpecification>> replacementSpecifications; 
     82 
     83    /** 
     84     * <p> 
     85     * initializes the parser with the file containing parsing parameters to be considered 
    9386     * </p> 
    9487     * 
    95      * @param parseParams the parsing parameters to be considered 
    96      */ 
    97     public HTMLLogParser(Map<String, List<String>> parseParams) { 
    98         this.parseParams = parseParams; 
    99          
    100         for (String paramKey : parseParams.keySet()) { 
    101             if (!"clearId".equals(paramKey) && !"clearIndex".equals(paramKey) && 
    102                 !"idReplacements".equals(paramKey)) 
    103             { 
    104                 throw new IllegalArgumentException("unknown parse parameter key " + paramKey); 
    105             } 
    106         } 
     88     * @param parseParamFile the parsing parameters to be considered 
     89     */ 
     90    public HTMLLogParser(String parseParamFile) { 
     91        this.parseParamFile = parseParamFile; 
    10792    } 
    10893 
     
    11499        throws SAXException 
    115100    { 
     101        ensureParsingParameters(); 
     102         
    116103        HTMLGUIElementSpec specification = null; 
    117104         
     
    182169                String htmlId = parameters.get("htmlid"); 
    183170                 
    184                 if (clearIndex(tagName, index, htmlId, parent)) { 
    185                     index = -1; 
    186                 } 
    187                  
    188                 String idReplacement = replaceHTMLId(tagName, index, htmlId, parent); 
    189                 if (idReplacement != null) { 
    190                     htmlId = idReplacement; 
    191                 } 
    192                 else if (clearHTMLId(tagName, index, htmlId, parent)) { 
    193                     htmlId = null; 
     171                String replacement = getReplacementMapping(tagName, index, htmlId, parent); 
     172                 
     173                if (replacement != null) { 
     174                    if (replacement.startsWith("CLEAR_INDEX,")) { 
     175                        index = -1; 
     176                        replacement = replacement.substring("CLEAR_INDEX,".length()); 
     177                    } 
     178                    else if ("CLEAR_INDEX".equals(replacement)) { 
     179                        index = -1; 
     180                        replacement = htmlId; 
     181                    } 
     182                     
     183                    if ("".equals(replacement)) { 
     184                        htmlId = null; 
     185                    } 
     186                    else { 
     187                        htmlId = replacement; 
     188                    } 
    194189                } 
    195190                 
     
    226221        } 
    227222    } 
    228  
    229     /** 
    230      * <p> 
    231      * checks if for a specific GUI element the index shall be ignored or not by considering the 
    232      * parsing parameters. 
     223     
     224    /** 
     225     * <p> 
     226     * returns the replacement mapping for the tag specified by the parameters, if a mapping exists. 
    233227     * </p> 
    234228     * 
    235      * @param tagName the tag of the considered GUI element 
    236      * @param index   the index of the GUI element 
    237      * @param id      the id of the GUI element 
    238      * @param parent  the parent GUI element of the considered GUI element 
     229     * @param tagName      the tag of the considered GUI element 
     230     * @param index        the index of the GUI element 
     231     * @param id           the id of the GUI element 
     232     * @param parent       the parent GUI element of the considered GUI element 
    239233     *  
    240      * @return true if the index shall be ignored, false else. 
    241      */ 
    242     private boolean clearIndex(String tagName, int index, String id, HTMLGUIElement parent) { 
    243         return clearSomething("clearIndex", tagName, index, id, parent); 
    244     } 
    245  
    246     /** 
    247      * <p> 
    248      * checks if the parsing parameters define a replacement for the id of the given GUI element 
    249      * and if so returns this replacement 
    250      * </p> 
    251      * 
    252      * @param tagName the tag of the considered GUI element 
    253      * @param index   the index of the GUI element 
    254      * @param id      the id of the GUI element 
    255      * @param parent  the parent GUI element of the considered GUI element 
    256      *  
    257      * @return the identified replacement 
    258      */ 
    259     private String replaceHTMLId(String tagName, int index, String htmlId, HTMLGUIElement parent) 
    260         throws SAXException 
     234     * @return the replacement mapping, if any is configured; null else 
     235     */ 
     236    private String getReplacementMapping(String         tagName, 
     237                                         int            index, 
     238                                         String         htmlId, 
     239                                         HTMLGUIElement parent) 
    261240    { 
    262         if ((idReplacements == null) && (parseParams.containsKey("idReplacements"))) { 
    263             idReplacements = new HashMap<String, String>(); 
    264             for (String fileName : parseParams.get("idReplacements")) { 
     241        List<ReplacementSpecification> mappingCandidates = replacementSpecifications.get(tagName); 
     242         
     243        if (mappingCandidates != null) { 
     244            for (ReplacementSpecification replacementSpec : mappingCandidates) { 
     245                if (replacementSpec.matches(tagName, index, htmlId, parent)) { 
     246                    return replacementSpec.getReplacement(); 
     247                } 
     248            } 
     249        } 
     250         
     251        return null; 
     252    } 
     253 
     254    /* (non-Javadoc) 
     255     * @see de.ugoe.cs.autoquest.plugin.html.AbstractDefaultLogParser#handleEvent(String, Map) 
     256     */ 
     257    @Override 
     258    protected boolean handleEvent(String type, Map<String, String> parameters) throws SAXException { 
     259        String targetId = parameters.get("target"); 
     260         
     261        if (targetId == null) { 
     262            if (replacementSpecifications.size() != 0) { 
     263                throw new SAXException 
     264                    ("old log file versions can not be parsed with parse parameters"); 
     265            } 
     266             
     267            String targetDocument = parameters.get("targetDocument"); 
     268            String targetDOMPath = parameters.get("targetDOMPath"); 
     269             
     270            if ((targetDocument == null) || (targetDOMPath == null)) { 
     271                throw new SAXException("event has no target defined"); 
     272            } 
     273             
     274            targetId = determineTargetId(targetDocument, targetDOMPath); 
     275             
     276            if (targetId == null) { 
     277                // the target id can not be determined yet 
     278                return false; 
     279            } 
     280        } 
     281         
     282        IGUIElement target = super.getGUIElementTree().find(targetId); 
     283         
     284        if (target == null) { 
     285            // event not processible yet 
     286            return false; 
     287        } 
     288 
     289        IEventType eventType = 
     290            HTMLEventTypeFactory.getInstance().getEventType(type, parameters, target); 
     291         
     292        if (eventType != null) { 
     293            Event event = new Event(eventType, target); 
     294 
     295            String timestampStr = parameters.get("timestamp"); 
     296         
     297            if (timestampStr != null) { 
     298                event.setTimestamp(Long.parseLong(timestampStr)); 
     299            } 
     300 
     301            ((HTMLGUIElement) event.getTarget()).markUsed(); 
     302         
     303            super.addToSequence(event); 
     304        } 
     305        // else ignore unknown event type 
     306 
     307        return true; 
     308    } 
     309 
     310    /** 
     311     * <p> 
     312     * reads parsing parameters from the config file and makes them available for the parsing 
     313     * process 
     314     * </p> 
     315     */ 
     316    private void ensureParsingParameters() throws SAXException { 
     317        if (replacementSpecifications == null) { 
     318            replacementSpecifications = new HashMap<String, List<ReplacementSpecification>>(); 
     319             
     320            if (parseParamFile != null) { 
    265321                Properties props = new Properties(); 
    266322                FileInputStream stream = null; 
    267323                try { 
    268                     stream = new FileInputStream(new File(fileName)); 
     324                    stream = new FileInputStream(new File(parseParamFile)); 
    269325                    props.load(stream); 
    270326                } 
    271327                catch (FileNotFoundException e) { 
    272                     throw new SAXException("could not find file " + fileName, e); 
     328                    throw new SAXException("could not find file " + parseParamFile, e); 
    273329                } 
    274330                catch (IOException e) { 
    275                     throw new SAXException("error reading file " + fileName, e); 
     331                    throw new SAXException("error reading file " + parseParamFile, e); 
    276332                } 
    277333                finally { 
     
    285341                    } 
    286342                } 
    287                  
     343 
    288344                for (Map.Entry<Object, Object> entry : props.entrySet()) { 
    289                     idReplacements.put((String) entry.getKey(), (String) entry.getValue()); 
    290                 } 
    291             } 
    292         } 
    293          
    294         if (idReplacements != null) { 
    295             for (Map.Entry<String, String> replacementSpec : idReplacements.entrySet()) { 
    296                 String tagSpec = replacementSpec.getKey(); 
    297  
    298                 if (tagSpec.startsWith("/")) { 
    299                     throw new IllegalArgumentException("can not handle absolute specifications"); 
    300                 } 
    301                 else if (tagSpec.endsWith("/")) { 
    302                     throw new IllegalArgumentException("specifications may not end with a /"); 
    303                 } 
    304  
    305                 String[] tagSpecs = tagSpec.split("/"); 
    306  
    307                 if (tagMatchesTagSpec(tagName, index, htmlId, parent, tagSpecs)) { 
    308                     return replacementSpec.getValue(); 
    309                 } 
    310             } 
    311         } 
    312          
    313         return null; 
    314     } 
    315  
    316     /** 
    317      * <p> 
    318      * checks if for a specific GUI element the id shall be ignored or not by considering the 
    319      * parsing parameters. 
    320      * </p> 
    321      * 
    322      * @param tagName the tag of the considered GUI element 
    323      * @param index   the index of the GUI element 
    324      * @param id      the id of the GUI element 
    325      * @param parent  the parent GUI element of the considered GUI element 
    326      *  
    327      * @return true if the id shall be ignored, false else. 
    328      */ 
    329     private boolean clearHTMLId(String tagName, int index, String id, HTMLGUIElement parent) { 
    330         return clearSomething("clearId", tagName, index, id, parent); 
     345                    ReplacementSpecification replSpec = new ReplacementSpecification 
     346                        ((String) entry.getKey(), (String) entry.getValue()); 
     347                     
     348                    List<ReplacementSpecification> similarReplSpecs = 
     349                        replacementSpecifications.get(replSpec.getLastTagName()); 
     350                     
     351                    if (similarReplSpecs == null) { 
     352                        similarReplSpecs = new LinkedList<ReplacementSpecification>(); 
     353                        replacementSpecifications.put(replSpec.getLastTagName(), similarReplSpecs); 
     354                    } 
     355                     
     356                    similarReplSpecs.add(replSpec); 
     357                } 
     358            } 
     359        } 
    331360    } 
    332361     
    333     /** 
    334      * <p> 
    335      * convenience method to check for the existence for specific parsing parameters for clearing 
    336      * ids or indexes of GUI elements. 
    337      * </p> 
    338      * 
    339      * @param parseParamId the id of the parsing parameter to be checked for the GUI element 
    340      * @param tagName      the tag of the considered GUI element 
    341      * @param index        the index of the GUI element 
    342      * @param id           the id of the GUI element 
    343      * @param parent       the parent GUI element of the considered GUI element 
    344      *  
    345      * @return true if the denoted parse parameter is set to ignore, false else. 
    346      */ 
    347     private boolean clearSomething(String         parseParamId, 
    348                                    String         tagName, 
    349                                    int            index, 
    350                                    String         id, 
    351                                    HTMLGUIElement parent) 
    352     { 
    353         if (parseParams.containsKey(parseParamId)) { 
    354             for (String spec : parseParams.get(parseParamId)) { 
    355                 // determine the specification parts 
    356                 if (spec.startsWith("/")) { 
    357                     throw new IllegalArgumentException("can not handle absolute specifications"); 
    358                 } 
    359                 else if (spec.endsWith("/")) { 
    360                     throw new IllegalArgumentException("specifications may not end with a /"); 
    361                 } 
    362                  
    363                 String[] tagSpecs = spec.split("/"); 
    364                  
    365                 if (tagMatchesTagSpec(tagName, index, id, parent, tagSpecs)) { 
    366                     return true; 
    367                 } 
    368             } 
    369         } 
    370          
    371         return false; 
    372     } 
    373  
    374     /** 
    375      * <p> 
    376      * convenience method to check if a given GUI element matches a specification tags provided 
    377      * through the parsing parameters. 
    378      * </p> 
    379      * 
    380      * @param tagName  the tag of the considered GUI element 
    381      * @param index    the index of the GUI element 
    382      * @param id       the id of the GUI element 
    383      * @param parent   the parent GUI element of the considered GUI element 
    384      * @param tagSpecs the specification of a GUI element to match against the given GUI element 
    385      *  
    386      * @return true if the denoted parse parameter is set to ignore, false else. 
    387      */ 
    388     private boolean tagMatchesTagSpec(String         tagName, 
    389                                       int            index, 
    390                                       String         id, 
    391                                       HTMLGUIElement parent, 
    392                                       String[]       tagSpecs) 
    393     { 
    394          
    395         if (tagSpecs.length > 0) { 
    396             Matcher matcher = htmlElementSpecPattern.matcher(tagSpecs[tagSpecs.length - 1]); 
    397              
    398             if (!matcher.matches()) { 
    399                 throw new IllegalArgumentException 
    400                     ("illegal tag specification " + tagSpecs[tagSpecs.length - 1]); 
    401             } 
    402              
    403             if (!tagName.equals(matcher.group(1))) { 
    404                 return false; 
    405             } 
    406              
    407             String idCondition = matcher.group(4); 
    408              
    409             if (idCondition != null) { 
    410                 if (!idCondition.equals(id)) { 
    411                     // check if the id condition would match with ignoring specific characters 
    412                     if ((id != null) && (idCondition.indexOf('#') > -1)) { 
    413                         // first of all, the length must match 
    414                         if (idCondition.length() != id.length()) { 
    415                             return false; 
    416                         } 
    417                          
    418                         for (int i = 0; i < idCondition.length(); i++) { 
    419                             if ((idCondition.charAt(i) != '#') && 
    420                                 (idCondition.charAt(i) != id.charAt(i))) 
    421                             { 
    422                                 // if there is a character that is neither ignored not matches 
    423                                 // the condition at a specific position, return "no match" 
    424                                 return false; 
    425                             } 
    426                         } 
    427                          
    428                     } 
    429                     else { 
    430                         // no condition ignoring specific characters 
    431                         return false; 
    432                     } 
    433                 } 
    434             } 
    435              
    436             String indexCondition = matcher.group(3); 
    437              
    438             if (indexCondition != null) { 
    439                 try { 
    440                     if (index != Integer.parseInt(indexCondition)) { 
    441                         return false; 
    442                     } 
    443                 } 
    444                 catch (NumberFormatException e) { 
    445                     throw new IllegalArgumentException 
    446                         ("illegal tag index specification " + indexCondition, e); 
    447                 } 
    448             } 
    449              
    450             if (tagSpecs.length > 1) { 
    451                 if (parent instanceof HTMLPageElement) { 
    452                     return tagMatchesTagSpec(((HTMLPageElement) parent).getTagName(), 
    453                                              ((HTMLPageElement) parent).getIndex(), 
    454                                              ((HTMLPageElement) parent).getHtmlId(), 
    455                                              (HTMLGUIElement) parent.getParent(), 
    456                                              Arrays.copyOfRange(tagSpecs, 0, tagSpecs.length - 1)); 
    457                 } 
    458                 else { 
    459                     throw new IllegalArgumentException 
    460                         ("specification matches documents or servers. This is not supported yet."); 
    461                 } 
    462             } 
    463             else { 
    464                 return true; 
    465             } 
    466         } 
    467         else { 
    468             return true; 
    469         } 
    470     } 
    471  
    472     /* (non-Javadoc) 
    473      * @see de.ugoe.cs.autoquest.plugin.html.AbstractDefaultLogParser#handleEvent(String, Map) 
    474      */ 
    475     @Override 
    476     protected boolean handleEvent(String type, Map<String, String> parameters) throws SAXException { 
    477         String targetId = parameters.get("target"); 
    478          
    479         if (targetId == null) { 
    480             if (parseParams.size() != 0) { 
    481                 throw new SAXException 
    482                     ("old log file versions can not be parsed with parse parameters"); 
    483             } 
    484              
    485             String targetDocument = parameters.get("targetDocument"); 
    486             String targetDOMPath = parameters.get("targetDOMPath"); 
    487              
    488             if ((targetDocument == null) || (targetDOMPath == null)) { 
    489                 throw new SAXException("event has no target defined"); 
    490             } 
    491              
    492             targetId = determineTargetId(targetDocument, targetDOMPath); 
    493              
    494             if (targetId == null) { 
    495                 // the target id can not be determined yet 
    496                 return false; 
    497             } 
    498         } 
    499          
    500         IGUIElement target = super.getGUIElementTree().find(targetId); 
    501          
    502         if (target == null) { 
    503             // event not processible yet 
    504             return false; 
    505         } 
    506  
    507         IEventType eventType = 
    508             HTMLEventTypeFactory.getInstance().getEventType(type, parameters, target); 
    509          
    510         if (eventType != null) { 
    511             Event event = new Event(eventType, target); 
    512  
    513             String timestampStr = parameters.get("timestamp"); 
    514          
    515             if (timestampStr != null) { 
    516                 event.setTimestamp(Long.parseLong(timestampStr)); 
    517             } 
    518  
    519             ((HTMLGUIElement) event.getTarget()).markUsed(); 
    520          
    521             super.addToSequence(event); 
    522         } 
    523         // else ignore unknown event type 
    524  
    525         return true; 
    526     } 
    527  
    528362    /** 
    529363     * <p> 
     
    647481    } 
    648482 
     483    /** 
     484     * <p>specification for a replacement consisting of path of tag or document specifications 
     485     * and the appropriate replacement.</p> 
     486     */ 
     487    private static class ReplacementSpecification { 
     488         
     489        /** 
     490         * <p> 
     491         * the pattern used for parsing parsing parameters 
     492         * </p> 
     493         */ 
     494        private Pattern htmlElementSpecPattern = Pattern.compile 
     495            ("(document\\(path=([\\w/-]+)\\))|((\\w+)(\\[(\\d+)\\]|\\(htmlId=([\\w-_#]+)\\))?)"); 
     496         
     497        /** 
     498         * <p> 
     499         * the path of specifications (tags and document) specifying the tag for which this 
     500         * replacement is specified 
     501         * </p> 
     502         */ 
     503        private List<Spec> specs = new LinkedList<Spec>(); 
     504         
     505        /** 
     506         * <p> 
     507         * the name of the last tag in the specification path (used for indexing purposes) 
     508         * </p> 
     509         */ 
     510        private String lastTagName; 
     511         
     512        /** 
     513         * <p> 
     514         * the configured replacement 
     515         * </p> 
     516         */ 
     517        private String replacement; 
     518 
     519        /** 
     520         * <p> 
     521         * initializes the specification with the key/value strings from the config file. Parses 
     522         * the key to get the specification path consisting of, optionally, a document 
     523         * specification and one or more tag specification. 
     524         * </p> 
     525         */ 
     526        public ReplacementSpecification(String tagSpec, String replacement) { 
     527            List<String> tagSpecs = split(tagSpec); 
     528             
     529            for (int i = 0; i < tagSpecs.size(); i++) { 
     530                Matcher matcher = htmlElementSpecPattern.matcher(tagSpecs.get(i)); 
     531                 
     532                if (!matcher.matches()) { 
     533                    throw new IllegalArgumentException 
     534                        ("illegal tag specification " + tagSpecs.get(i)); 
     535                } 
     536                 
     537                if (matcher.group(1) != null) { 
     538                    this.specs.add(new DocumentSpec(matcher.group(2))); 
     539                } 
     540                else if (matcher.group(4) != null) { 
     541                    String indexConditionStr = matcher.group(6); 
     542                    Integer indexCondition = null; 
     543                 
     544                    if (indexConditionStr != null) { 
     545                        try { 
     546                            indexCondition = Integer.parseInt(indexConditionStr); 
     547                        } 
     548                        catch (NumberFormatException e) { 
     549                            throw new IllegalArgumentException 
     550                                ("illegal tag index specification " + indexConditionStr, e); 
     551                        } 
     552                    } 
     553                 
     554                    this.specs.add 
     555                        (new TagSpec(matcher.group(4), indexCondition, matcher.group(7))); 
     556                } 
     557            } 
     558             
     559            this.lastTagName = ((TagSpec) this.specs.get(this.specs.size() - 1)).getTagName(); 
     560             
     561            this.replacement = replacement; 
     562        } 
     563 
     564        /** 
     565         * <p> 
     566         * convenience method to split the key of a key/value pair from the config file into its 
     567         * parts 
     568         * </p> 
     569         */ 
     570        private List<String> split(String tagSpec) { 
     571            List<String> specs = new LinkedList<String>(); 
     572             
     573            StringBuffer currentSpec = new StringBuffer(); 
     574            int openBraces = 0; 
     575             
     576            for (int i = 0; i < tagSpec.length(); i++) { 
     577                char curChar = tagSpec.charAt(i); 
     578                if ((openBraces == 0) && ('/' == curChar) && (currentSpec.length() > 0)) { 
     579                    specs.add(currentSpec.toString()); 
     580                    currentSpec.setLength(0); 
     581                } 
     582                else { 
     583                    if ('(' == curChar) { 
     584                        openBraces++; 
     585                    } 
     586                    else if (')' == curChar) { 
     587                        openBraces--; 
     588                    } 
     589                    currentSpec.append(curChar); 
     590                } 
     591            } 
     592             
     593            if (currentSpec.length() > 0) { 
     594                specs.add(currentSpec.toString()); 
     595            } 
     596             
     597            return specs; 
     598        } 
     599 
     600        /** 
     601         * <p> 
     602         * checks, if the tag identified by the parameters matches this specificaiton. 
     603         * </p> 
     604         */ 
     605        private boolean matches(String tagName, int index, String htmlId, HTMLGUIElement parent) { 
     606            String currentTagName = tagName; 
     607            int currentIndex = index; 
     608            String currentHtmlId = htmlId; 
     609            String currentPath = null; 
     610            HTMLGUIElement currentParent = parent; 
     611             
     612            int i = specs.size() - 1; 
     613             
     614            while (i >= 0) { 
     615                if ((specs.get(i) instanceof TagSpec) && 
     616                    (!((TagSpec) specs.get(i)).matches(currentTagName, currentIndex, currentHtmlId))) 
     617                { 
     618                    return false; 
     619                } 
     620                else if ((specs.get(i) instanceof DocumentSpec) && 
     621                         (!((DocumentSpec) specs.get(i)).matches(currentPath))) 
     622                { 
     623                    return false; 
     624                } 
     625                 
     626                i--; 
     627                 
     628                if (i >= 0) { 
     629                    if (currentParent instanceof HTMLPageElement) { 
     630                        currentTagName = ((HTMLPageElement) currentParent).getTagName(); 
     631                        currentIndex = ((HTMLPageElement) currentParent).getIndex(); 
     632                        currentHtmlId = ((HTMLPageElement) currentParent).getHtmlId(); 
     633                        currentPath = null; 
     634                        currentParent = (HTMLGUIElement) currentParent.getParent(); 
     635                     } 
     636                    else if (currentParent instanceof HTMLDocument) { 
     637                        currentTagName = null; 
     638                        currentIndex = Integer.MIN_VALUE; 
     639                        currentHtmlId = null; 
     640                        currentPath = ((HTMLDocument) currentParent).getPath(); 
     641                        currentParent = (HTMLGUIElement) currentParent.getParent(); 
     642                    } 
     643                    else { 
     644                        throw new IllegalArgumentException 
     645                            ("specification matches documents or servers. This is not supported yet."); 
     646                    } 
     647                } 
     648            } 
     649             
     650            return true; 
     651        } 
     652 
     653        /** 
     654         * <p> 
     655         * returns the specified replacement 
     656         * </p> 
     657         */ 
     658        private String getReplacement() { 
     659            return replacement; 
     660        } 
     661 
     662        /** 
     663         * <p> 
     664         * returns the name of the last tag specified in the specification path 
     665         * </p> 
     666         */ 
     667        private String getLastTagName() { 
     668            return lastTagName; 
     669        } 
     670 
     671        /* (non-Javadoc) 
     672         * @see java.lang.Object#toString() 
     673         */ 
     674        @Override 
     675        public String toString() { 
     676            StringBuffer result = new StringBuffer(); 
     677            for (Spec spec : specs) { 
     678                if (result.length() > 0) { 
     679                    result.append("/"); 
     680                } 
     681                result.append(spec); 
     682            } 
     683             
     684            result.append('='); 
     685            result.append(replacement); 
     686             
     687            return result.toString(); 
     688        } 
     689         
     690    } 
     691 
     692    /** 
     693     * <p> 
     694     * parent type for document and tag specifications 
     695     * </p> 
     696     */ 
     697    private static interface Spec { } 
     698 
     699    /** 
     700     * <p> 
     701     * specification of a document 
     702     * </p> 
     703     */ 
     704    private static class DocumentSpec implements Spec { 
     705         
     706        /** 
     707         * <p> 
     708         * the part of the path the document path must have to match this specification 
     709         * </p> 
     710         */ 
     711        private String pathPart; 
     712 
     713        /** 
     714         * <p> 
     715         * initializes the document specification with the path part 
     716         * </p> 
     717         */ 
     718        private DocumentSpec(String pathPart) { 
     719            this.pathPart = pathPart; 
     720        } 
     721 
     722        /** 
     723         * <p> 
     724         * returns true if the provided path contains the path part provided to the parameter 
     725         * </p> 
     726         */ 
     727        private boolean matches(String path) { 
     728            return path.contains(pathPart); 
     729        } 
     730 
     731        /* (non-Javadoc) 
     732         * @see java.lang.Object#toString() 
     733         */ 
     734        @Override 
     735        public String toString() { 
     736            return "document(path=" + pathPart + ")"; 
     737        } 
     738    } 
     739 
     740    /** 
     741     * <p> 
     742     * specification for a tag containing a tag name and either an index or id condition. 
     743     * </p> 
     744     */ 
     745    private static class TagSpec implements Spec { 
     746 
     747        /** 
     748         * <p> 
     749         * the name of the tag to match 
     750         * </p> 
     751         */ 
     752        private String tagName; 
     753         
     754        /** 
     755         * <p> 
     756         * the index of the tag to match 
     757         * </p> 
     758         */ 
     759        private Integer indexCondition; 
     760         
     761        /** 
     762         * <p> 
     763         * the id of the tag to match 
     764         * </p> 
     765         */ 
     766        private String idCondition; 
     767 
     768        /** 
     769         * <p> 
     770         * initializes the specification with all required parameters 
     771         * </p> 
     772         */ 
     773        private TagSpec(String tagName, Integer indexCondition, String idCondition) { 
     774            this.tagName = tagName; 
     775            this.indexCondition = indexCondition; 
     776            this.idCondition = idCondition; 
     777        } 
     778 
     779        /** 
     780         * <p> 
     781         * returns true if the provided tag information matches this specification. The id is 
     782         * checked first. If the id condition has a # at some position, the respective element 
     783         * of the provided id is ignored. 
     784         * </p> 
     785         */ 
     786        private boolean matches(String tagName, int index, String htmlId) { 
     787            if (!this.tagName.equals(tagName)) { 
     788                return false; 
     789            } 
     790             
     791            if (idCondition != null) { 
     792                if (!idCondition.equals(htmlId)) { 
     793                    // check if the id condition would match with ignoring specific characters 
     794                    if ((htmlId != null) && (idCondition.indexOf('#') > -1)) { 
     795                        // first of all, the length must match 
     796                        if (idCondition.length() != htmlId.length()) { 
     797                            return false; 
     798                        } 
     799                         
     800                        for (int i = 0; i < idCondition.length(); i++) { 
     801                            if ((idCondition.charAt(i) != '#') && 
     802                                (idCondition.charAt(i) != htmlId.charAt(i))) 
     803                            { 
     804                                // if there is a character that is neither ignored nor matches 
     805                                // the condition at a specific position, return "no match" 
     806                                return false; 
     807                            } 
     808                        } 
     809                         
     810                    } 
     811                    else { 
     812                        // no condition ignoring specific characters 
     813                        return false; 
     814                    } 
     815                } 
     816            } 
     817             
     818            if ((indexCondition != null) && (index != indexCondition)) { 
     819                return false; 
     820            } 
     821             
     822            return true; 
     823        } 
     824 
     825        /** 
     826         * <p> 
     827         * returns the name of the tags matched by this specification 
     828         * </p> 
     829         */ 
     830        private String getTagName() { 
     831            return tagName; 
     832        } 
     833 
     834        /* (non-Javadoc) 
     835         * @see java.lang.Object#toString() 
     836         */ 
     837        @Override 
     838        public String toString() { 
     839            StringBuffer result = new StringBuffer(tagName); 
     840             
     841            if (idCondition != null) { 
     842                result.append("(htmlId="); 
     843                result.append(idCondition); 
     844                result.append(')'); 
     845            } 
     846            else if (indexCondition != null) { 
     847                result.append('['); 
     848                result.append(indexCondition); 
     849                result.append(']'); 
     850            } 
     851             
     852            return result.toString(); 
     853        } 
     854         
     855    } 
    649856} 
  • trunk/autoquest-plugin-html/src/main/java/de/ugoe/cs/autoquest/plugin/html/commands/CMDcorrectHTMLLogDirs.java

    r1339 r1496  
    1717import java.io.File; 
    1818import java.util.Arrays; 
    19 import java.util.HashMap; 
    2019import java.util.List; 
    2120import java.util.logging.Level; 
     
    9190            String serverName = null; 
    9291             
    93             HTMLLogParser parser = new HTMLLogParser(new HashMap<String, List<String>>()); 
     92            HTMLLogParser parser = new HTMLLogParser(null); 
    9493            try { 
    9594                parser.parseFile(file); 
  • trunk/autoquest-plugin-html/src/main/java/de/ugoe/cs/autoquest/plugin/html/commands/CMDparseDirHTML.java

    r1435 r1496  
    1818import java.util.Arrays; 
    1919import java.util.Collection; 
    20 import java.util.HashMap; 
    21 import java.util.LinkedList; 
    2220import java.util.List; 
    23 import java.util.Map; 
    2421import java.util.logging.Level; 
    2522import java.util.regex.Matcher; 
     
    5552        String path = null; 
    5653        String sequencesName = null; 
    57         Map<String, List<String>> parseParams = new HashMap<String, List<String>>(); 
     54        String parseParamFile = null; 
    5855 
    5956        try { 
     
    6966                } 
    7067                else { 
    71                     Pattern parseParamPattern = Pattern.compile("-(\\w*)=([\\w=\\[\\]\\(\\)/\\.-]*)"); 
     68                    Pattern parseParamPattern = Pattern.compile("-(\\w*)=([\\w/\\.-]*)"); 
    7269                    Matcher matcher = parseParamPattern.matcher(param); 
    7370                     
    7471                    if (matcher.matches()) { 
    7572                        String key = matcher.group(1); 
    76                         List<String> values = parseParams.get(key); 
    77                          
    78                         if (values == null) { 
    79                             values = new LinkedList<String>(); 
    80                             parseParams.put(key, values); 
     73                        if (!"parseParamFile".equals(key)) { 
     74                            String message = "unknown parameter: " + key; 
     75                            Console.printerrln(message); 
     76                            throw new IllegalArgumentException(message); 
    8177                        } 
    8278                         
    83                         values.add(matcher.group(2)); 
     79                        parseParamFile = matcher.group(2); 
    8480                    } 
    8581                    else { 
    86                         String message = "parse parameter does not follow format: -<key>=<value>"; 
     82                        String message = "parameter does not follow format: -<key>=<value>"; 
    8783                        Console.printerrln(message); 
    8884                        throw new IllegalArgumentException(message); 
     
    105101        } 
    106102 
    107         HTMLLogParser parser = new HTMLLogParser(parseParams); 
     103        HTMLLogParser parser = new HTMLLogParser(parseParamFile); 
    108104 
    109105        parseFile(folder, parser); 
     
    161157    @Override 
    162158    public String help() { 
    163         return "parseDirHTML <directory> [<sequencesName>] " + 
    164             "{-idReplacements=path/to/replacementfile} {-clearId=path/to[0]/gui(htmlId=element)} " + 
    165             "{-clearIndex=path/to[0]/gui(htmlId=element)}"; 
     159        return "parseDirHTML <directory> [<sequencesName>] {-parseParams=path/to/parseParamsFile}"; 
    166160    } 
    167161 
  • trunk/autoquest-plugin-html/src/main/java/de/ugoe/cs/autoquest/plugin/html/commands/CMDparseHTML.java

    r1435 r1496  
    1616 
    1717import java.util.Collection; 
    18 import java.util.HashMap; 
    19 import java.util.LinkedList; 
    2018import java.util.List; 
    21 import java.util.Map; 
    2219import java.util.regex.Matcher; 
    2320import java.util.regex.Pattern; 
     
    5047        String filename = null; 
    5148        String sequencesName = null; 
    52         Map<String, List<String>> parseParams = new HashMap<String, List<String>>(); 
     49        String parseParamFile = null; 
    5350 
    5451        try { 
     
    6461                } 
    6562                else { 
    66                     Pattern parseParamPattern = Pattern.compile("-(\\w*)=([\\w=\\[\\]\\(\\)/\\.-]*)"); 
     63                    Pattern parseParamPattern = Pattern.compile("-(\\w*)=([\\w/\\.-]*)"); 
    6764                    Matcher matcher = parseParamPattern.matcher(param); 
    6865                     
    6966                    if (matcher.matches()) { 
    7067                        String key = matcher.group(1); 
    71                         List<String> values = parseParams.get(key); 
    72                          
    73                         if (values == null) { 
    74                             values = new LinkedList<String>(); 
    75                             parseParams.put(key, values); 
     68                        if (!"parseParamFile".equals(key)) { 
     69                            String message = "unknown parameter: " + key; 
     70                            Console.printerrln(message); 
     71                            throw new IllegalArgumentException(message); 
    7672                        } 
    7773                         
    78                         values.add(matcher.group(2)); 
     74                        parseParamFile = matcher.group(2); 
    7975                    } 
    8076                    else { 
    81                         String message = "parse parameter does not follow format: -<key>=<value>"; 
     77                        String message = "parameter does not follow format: -<key>=<value>"; 
    8278                        Console.printerrln(message); 
    8379                        throw new IllegalArgumentException(message); 
     
    9490        } 
    9591 
    96         HTMLLogParser parser = new HTMLLogParser(parseParams); 
     92        HTMLLogParser parser = new HTMLLogParser(parseParamFile); 
    9793 
    9894        try { 
     
    124120    @Override 
    125121    public String help() { 
    126         return "parseHTML <filename> [<sequencesName>] " + 
    127             "{-idReplacements=path/to/replacementfile} {-clearId=path/to[0]/gui(htmlId=element)} " + 
    128             "{-clearIndex=path/to[0]/gui(htmlId=element)}"; 
     122        return "parseHTML <filename> [<sequencesName>] {-parseParams=path/to/parseParamsFile}"; 
    129123    } 
    130124 
  • trunk/autoquest-plugin-html/src/main/java/de/ugoe/cs/autoquest/plugin/html/guimodel/HTMLPageElement.java

    r1490 r1496  
    9090        } 
    9191        else if (otherElement instanceof HTMLPageElement) { 
    92             if (this == otherElement) { 
     92            if (equals(otherElement)) { 
    9393                return DISTANCE_NONE; 
    9494            } 
  • trunk/autoquest-plugin-html/src/main/resources/manuals/parseDirHTML

    r1354 r1496  
    11Treats all files in a directory structure as HTML log files and parses them into event sequences and a GUI model. Also sub directories are parsed. 
    22 
    3 The parsing process can be parameterized. This allows to replace or ignore ids or indexes of GUI elements in the log files. If they are replaced or ignored, the GUI model is more harmonized and GUI elements are considered equal although they are not. This may be helpful, e.g., if you have a table where each row is semantically the same. Without replacing or ignoring indexes or ids of the rows, each row is treated separately. But with replaced or ignored indexes or ids, all rows are considered the same. 
     3The parsing process can be parameterized. This allows to replace ids or ignore indexes of GUI elements in the log files. If they are replaced or ignored, the GUI model is more harmonized and GUI elements are considered equal although they are not. This may be helpful, e.g., if you have a table where each row is semantically the same. Without ignoring indexes or ids of the rows, each row is treated separately. But with ignored or replaced indexes or ids, all rows are considered the same. 
    44 
    5 To ignore the indexes, add -clearIndex=<path to GUI element> as parameter to the command call. To ignore ids, add -clearId=<path to GUI element> to the command call. The path to the GUI element is written using the HTML tag names and either their index or their id as identification. E.g., to denote all rows in a table where the table has the id "table_1" you can specify "table(htmlId=table_1)/tbody/tr". To denote e.g. all divs being the child of a div with an index 1, you specify "div[1]/div".   
     5The parameterization is done in a separate properties file. The keys in the file specify the tags for which either the id shall be replaced or the index shall be ignored. A specification for a simple tag either simply by its name, by its name and index or by its name and id looks as follows: 
    66 
    7 To replace ids, a separate files with mappings must be created. The path to this file must be provided using the idReplacements parameter. The file follows a typical properties format. The key is the path denoting the GUI element of which the id shall be set. The value is the actual id. The key may contain the # character to denote a wildcard in html ids. This allows matching several GUI elements with similar ids at once and to give them the same id. An example entry of this file is: 
     7tagName 
     8tagName[index] 
     9tagName(htmlId\=id) 
    810 
    9 div(htmlId\=id_number_#)=div_number_X 
     11Furthermore, tags can be specified as paths through the DOM in that several tags specifications are given and concatenated using /. An example with three specified tag (tag1 with index 5, tag 2, and tag 3 with id "id") is the following: 
    1012 
    11 This line would give all divs with an id "id_number_#" where # denotes any character the new id "div_number_X". Please note that for specifying the keys, it is required to escape any = sign in the key specification. This is usually required if the path to the denoted GUI elements denotes elements by their id as shown in the example. 
     13tag1[5]/tag2/tag3(htmlId\=id) 
    1214 
     15The specification of a tag id may contain the # character to denote a wildcard. This allows matching several GUI elements with similar ids at once and to give them the same id. An example entry of this is: 
     16 
     17div(htmlId\=id_number_#) 
     18 
     19This line would match all divs with an id starting with "id_number_" where # denotes any character. 
     20 
     21It is also possible to specify the document in which the tag path should match. A document is specified by giving a part of the documents path in the URL. After the document specification, the full path to the specified tag must be given. An example is the following: 
     22 
     23document(path\=accounts)/html/body/div[0]/ul/li(htmlId\=breadcrumb1)/a 
     24 
     25Please note that for specifying the keys, it is required to escape any = sign in the key specification. This is usually required if the path to the denoted GUI elements denotes elements by their id as shown in the example. 
     26 
     27To remove the id of a specified tag, the value must be empty. To set the id, the value must the id the tag shall have. To clear the index of the specified tag, that value must be CLEAR_INDEX. Here are some further example entries: 
     28 
     29body/div/div/div/form= 
     30body/p/small/a=imprint-link 
     31document(path\=accounts/login)/html/body/div[0]/div[1]/div[0]/form/p/a=password-reset-link 
     32document(path\=accounts/login)/html/body/div[0]/div[1]/div[0]/form/div/button=CLEAR_INDEX 
     33body/div[5]=date-chooser 
     34div(htmlId\=date-chooser)/div[0]=date-chooser_day 
    1335 
    1436 
     
    1941[<sequenceNames>] 
    2042    array of sequences into which the parsed events shall be stored 
    21 {-idReplacements=path/to/replacementfile} 
     43{-parseParams=path/to/replacementfile} 
    2244    used to define id replacements as described in a separate file 
    23 {-clearId=path/to[0]/gui(htmlId=element)} 
    24     used to define GUI elements of which the ids shall be ignored 
    25 {-clearIndex=path/to[0]/gui(htmlId=element)} 
    26     used to define GUI elements of which the indexes shall be ignored 
    2745 
    2846Example(s): 
    2947parseDirHTML /path/to/directory 
    30 parseDirHTML /path/to/directory sequences -clearId=table(htmlId=overview)/tbody[0]/tr 
    31 parseDirHTML /path/to/directory sequences -idReplacements=idReplacements.txt -clearId=body 
     48parseDirHTML /path/to/directory sequences -parseParams=idReplacements.txt 
  • trunk/autoquest-plugin-html/src/main/resources/manuals/parseHTML

    r1354 r1496  
    11Parses an HTML log file them into an event sequence and a GUI model. 
    22 
    3 The parsing process can be parameterized. This allows to replace or ignore ids or indexes of GUI elements in the log files. If they are replaced or ignored, the GUI model is more harmonized and GUI elements are considered equal although they are not. This may be helpful, e.g., if you have a table where each row is semantically the same. Without ignoring indexes or ids of the rows, each row is treated separately. But with ignored or replaced indexes or ids, all rows are considered the same. 
     3The parsing process can be parameterized. This allows to replace ids or ignore indexes of GUI elements in the log files. If they are replaced or ignored, the GUI model is more harmonized and GUI elements are considered equal although they are not. This may be helpful, e.g., if you have a table where each row is semantically the same. Without ignoring indexes or ids of the rows, each row is treated separately. But with ignored or replaced indexes or ids, all rows are considered the same. 
    44 
    5 To ignore the indexes, add -clearIndex=<path to GUI element> as parameter to the command call. To ignore ids, add -clearId=<path to GUI element> to the command call. The path to the GUI element is written using the HTML tag names and either their index or their id as identification. E.g., to denote all rows in a table where the table has the id "table_1" you can specify "table(htmlId=table_1)/tbody/tr". To denote e.g. all divs being the child of a div with an index 1, you specify "div[1]/div".   
     5The parameterization is done in a separate properties file. The keys in the file specify the tags for which either the id shall be replaced or the index shall be ignored. A specification for a simple tag either simply by its name, by its name and index or by its name and id looks as follows: 
    66 
    7 To replace ids, a separate files with mappings must be created. The path to this file must be provided using the idReplacements parameter. The file follows a typical properties format. The key is the path denoting the GUI element of which the id shall be set. The value is the actual id. The key may contain the # character to denote a wildcard in html ids. This allows matching several GUI elements with similar ids at once and to give them the same id. An example entry of this file is: 
     7tagName 
     8tagName[index] 
     9tagName(htmlId\=id) 
    810 
    9 div(htmlId\=id_number_#)=div_number_X 
     11Furthermore, tags can be specified as paths through the DOM in that several tags specifications are given and concatenated using /. An example with three specified tag (tag1 with index 5, tag 2, and tag 3 with id "id") is the following: 
    1012 
    11 This line would give all divs with an id "id_number_#" where # denotes any character the new id "div_number_X". Please note that for specifying the keys, it is required to escape any = sign in the key specification. This is usually required if the path to the denoted GUI elements denotes elements by their id as shown in the example. 
     13tag1[5]/tag2/tag3(htmlId\=id) 
    1214 
     15The specification of a tag id may contain the # character to denote a wildcard. This allows matching several GUI elements with similar ids at once and to give them the same id. An example entry of this is: 
    1316 
     17div(htmlId\=id_number_#) 
     18 
     19This line would match all divs with an id starting with "id_number_" where # denotes any character. 
     20 
     21It is also possible to specify the document in which the tag path should match. A document is specified by giving a part of the documents path in the URL. After the document specification, the full path to the specified tag must be given. An example is the following: 
     22 
     23document(path\=accounts)/html/body/div[0]/ul/li(htmlId\=breadcrumb1)/a 
     24 
     25Please note that for specifying the keys, it is required to escape any = sign in the key specification. This is usually required if the path to the denoted GUI elements denotes elements by their id as shown in the example. 
     26 
     27To remove the id of a specified tag, the value must be empty. To set the id, the value must the id the tag shall have. To clear the index of the specified tag, that value must be CLEAR_INDEX. Here are some further example entries: 
     28 
     29body/div/div/div/form= 
     30body/p/small/a=imprint-link 
     31document(path\=accounts/login)/html/body/div[0]/div[1]/div[0]/form/p/a=password-reset-link 
     32document(path\=accounts/login)/html/body/div[0]/div[1]/div[0]/form/div/button=CLEAR_INDEX 
     33body/div[5]=date-chooser 
     34div(htmlId\=date-chooser)/div[0]=date-chooser_day 
    1435 
    1536$USAGE$ 
     
    1940[<sequenceNames>] 
    2041    array of sequences into which the parsed events shall be stored 
    21 {-idReplacements=path/to/replacementfile} 
     42{-parseParams=path/to/replacementfile} 
    2243    used to define id replacements as described in a separate file 
    23 {-clearId=path/to[0]/gui(htmlId=element)} 
    24     used to define GUI elements of which the ids shall be ignored 
    25 {-clearIndex=path/to[0]/gui(htmlId=element)} 
    26     used to define GUI elements of which the indexes shall be ignored 
    2744 
    2845Example(s): 
    2946parseDirHTML /path/to/file.log 
    30 parseDirHTML /path/to/file.log sequences -clearId=table(htmlId=overview)/tbody[0]/tr 
    31 parseDirHTML /path/to/directory sequences -idReplacements=idReplacements.txt -clearId=body 
     47parseDirHTML /path/to/file.log sequences -parseParams=idReplacements.txt 
Note: See TracChangeset for help on using the changeset viewer.