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
File:
1 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} 
Note: See TracChangeset for help on using the changeset viewer.