source: trunk/autoquest-plugin-html/src/main/java/de/ugoe/cs/autoquest/plugin/html/HTMLLogParser.java @ 1496

Last change on this file since 1496 was 1496, checked in by pharms, 10 years ago
  • adapted parsing of HTML files to have more power in specifying replacements
  • Property svn:mime-type set to text/plain
File size: 29.3 KB
Line 
1//   Copyright 2012 Georg-August-Universität Göttingen, Germany
2//
3//   Licensed under the Apache License, Version 2.0 (the "License");
4//   you may not use this file except in compliance with the License.
5//   You may obtain a copy of the License at
6//
7//       http://www.apache.org/licenses/LICENSE-2.0
8//
9//   Unless required by applicable law or agreed to in writing, software
10//   distributed under the License is distributed on an "AS IS" BASIS,
11//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//   See the License for the specific language governing permissions and
13//   limitations under the License.
14
15package de.ugoe.cs.autoquest.plugin.html;
16
17import java.io.File;
18import java.io.FileInputStream;
19import java.io.FileNotFoundException;
20import java.io.IOException;
21import java.util.HashMap;
22import java.util.LinkedList;
23import java.util.List;
24import java.util.Map;
25import java.util.Properties;
26import java.util.regex.Matcher;
27import java.util.regex.Pattern;
28
29import org.xml.sax.SAXException;
30
31import de.ugoe.cs.autoquest.eventcore.Event;
32import de.ugoe.cs.autoquest.eventcore.IEventType;
33import de.ugoe.cs.autoquest.eventcore.guimodel.GUIModel;
34import de.ugoe.cs.autoquest.eventcore.guimodel.GUIModelException;
35import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement;
36import de.ugoe.cs.autoquest.plugin.html.eventcore.HTMLEventTypeFactory;
37import de.ugoe.cs.autoquest.plugin.html.guimodel.HTMLDocument;
38import de.ugoe.cs.autoquest.plugin.html.guimodel.HTMLDocumentSpec;
39import de.ugoe.cs.autoquest.plugin.html.guimodel.HTMLGUIElement;
40import de.ugoe.cs.autoquest.plugin.html.guimodel.HTMLGUIElementSpec;
41import de.ugoe.cs.autoquest.plugin.html.guimodel.HTMLPageElement;
42import de.ugoe.cs.autoquest.plugin.html.guimodel.HTMLPageElementSpec;
43import de.ugoe.cs.autoquest.plugin.html.guimodel.HTMLServerSpec;
44
45/**
46 * <p>
47 * This class provides the functionality to parse XML log files generated by the HTMLMonitor of
48 * AutoQUEST. The result of parsing a file is a collection of event sequences and a GUI model.
49 * </p>
50 * <p>
51 * The parser can be configured with parsing parameters to adapt, e.g., ids or or ignore indexes of
52 * parsed GUI elements. Details can be found in the manual pages of the respective parsing commands.
53 * </p>
54 *
55 * @author Fabian Glaser, Patrick Harms
56 * @version 1.0
57 *
58 */
59public class HTMLLogParser extends AbstractDefaultLogParser {
60   
61    /**
62     * <p>
63     * the pattern used for parsing HTML GUI element paths
64     * </p>
65     */
66    private Pattern htmlElementPattern =
67        Pattern.compile("(\\w+)(\\[(\\d+)\\]|\\(htmlId=([\\w-]+)\\))");
68   
69    /**
70     * <p>
71     * file containing parameters to influence parsing
72     * </p>
73     */
74    private String parseParamFile;
75
76    /**
77     * <p>
78     * a map containing replacement specifications for ids of GUI elements
79     * </p>
80     */
81    private Map<String, List<ReplacementSpecification>> replacementSpecifications;
82
83    /**
84     * <p>
85     * initializes the parser with the file containing parsing parameters to be considered
86     * </p>
87     *
88     * @param parseParamFile the parsing parameters to be considered
89     */
90    public HTMLLogParser(String parseParamFile) {
91        this.parseParamFile = parseParamFile;
92    }
93
94    /* (non-Javadoc)
95     * @see de.ugoe.cs.autoquest.plugin.html.AbstractDefaultLogParser#handleGUIElement(String, Map)
96     */
97    @Override
98    protected boolean handleGUIElement(String id, Map<String, String> parameters)
99        throws SAXException
100    {
101        ensureParsingParameters();
102       
103        HTMLGUIElementSpec specification = null;
104       
105        String parentId = parameters.get("parent");
106        HTMLGUIElement parent = (HTMLGUIElement) super.getGUIElementTree().find(parentId);
107
108        if (parameters.containsKey("host")) {
109            // this is a server specification
110            int port = 80;
111            String portStr = parameters.get("port");
112           
113            if (portStr != null) {
114                port = Integer.parseInt(portStr);
115            }
116           
117            specification = new HTMLServerSpec(parameters.get("host"), port);
118        }
119        else if (parameters.containsKey("path")) {
120            // this is a document specification
121           
122            if (parent != null) {
123                if (!(parent.getSpecification() instanceof HTMLServerSpec)) {
124                    throw new SAXException
125                        ("invalid log: parent GUI element of a document is not of type server");
126                }
127               
128                specification = new HTMLDocumentSpec
129                    ((HTMLServerSpec) parent.getSpecification(), parameters.get("path"),
130                     parameters.get("query"), parameters.get("title"));
131            }
132            else if (parentId == null) {
133                throw new SAXException("invalid log: a document has no parent id");
134            }
135        }
136        else if (parameters.containsKey("tagname")) {
137            String tagName = parameters.get("tagname");
138           
139            if (!tagNameMustBeConsidered(tagName)) {
140                return true;
141            }
142
143            if (parent != null) {
144                if (!childrenMustBeConsidered(parent)) {
145                    return true;
146                }
147               
148                IGUIElement document = parent;
149               
150                while ((document != null) &&
151                       (!(document.getSpecification() instanceof HTMLDocumentSpec)))
152                {
153                    document = document.getParent();
154                }
155               
156                if (document == null) {
157                    throw new SAXException
158                        ("invalid log: parent hierarchy of a page element does not contain a " +
159                         "document");
160                }
161               
162                int index = -1;
163                String indexStr = parameters.get("index");
164
165                if ((indexStr != null) && (!"".equals(indexStr))) {
166                    index = Integer.parseInt(indexStr);
167                }
168               
169                String htmlId = parameters.get("htmlid");
170               
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                    }
189                }
190               
191                if ((htmlId == null) && (index == -1)) {
192                    // set at least a default index, if all is to be ignored.
193                    index = 0;
194                }
195
196                specification = new HTMLPageElementSpec
197                    ((HTMLDocumentSpec) document.getSpecification(),
198                     tagName.intern(), htmlId == null ? null : htmlId.intern(), index);
199               
200            }
201            else if (parentId == null) {
202                throw new SAXException("invalid log: a page element has no parent id");
203            }
204        }
205        else {
206            throw new SAXException("invalid log: unknown GUI element");
207        }
208
209        if (specification != null) {
210            try {
211                super.getGUIElementTree().add(id, parentId, specification);
212            }
213            catch (GUIModelException e) {
214                throw new SAXException("could not handle GUI element with id " +
215                                       id + ": " + e.getMessage(), e);
216            }
217            return true;
218        }
219        else {
220            return false;
221        }
222    }
223   
224    /**
225     * <p>
226     * returns the replacement mapping for the tag specified by the parameters, if a mapping exists.
227     * </p>
228     *
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
233     *
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)
240    {
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) {
321                Properties props = new Properties();
322                FileInputStream stream = null;
323                try {
324                    stream = new FileInputStream(new File(parseParamFile));
325                    props.load(stream);
326                }
327                catch (FileNotFoundException e) {
328                    throw new SAXException("could not find file " + parseParamFile, e);
329                }
330                catch (IOException e) {
331                    throw new SAXException("error reading file " + parseParamFile, e);
332                }
333                finally {
334                    if (stream != null) {
335                        try {
336                            stream.close();
337                        }
338                        catch (IOException e) {
339                            // ignore
340                        }
341                    }
342                }
343
344                for (Map.Entry<Object, Object> entry : props.entrySet()) {
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        }
360    }
361   
362    /**
363     * <p>
364     * used to determine the id of a target denoted by an event. This is only required for older
365     * document formats. The new formats use concrete ids.
366     * </p>
367     */
368    private String determineTargetId(String targetDocument, String targetDOMPath)
369        throws SAXException
370    {
371        IGUIElement document = super.getGUIElementTree().find(targetDocument);
372       
373        if (document == null) {
374            return null;
375        }
376       
377        if (!(document.getSpecification() instanceof HTMLDocumentSpec)) {
378            throw new SAXException("an id that should refer to an HTML document refers to" +
379                                   "something else");
380        }
381       
382        GUIModel model = super.getGUIElementTree().getGUIModel();
383        IGUIElement child = document;
384        String[] pathElements = targetDOMPath.split("/");
385        int pathIndex = 0;
386       
387        HTMLPageElementSpec compareSpec;
388        String tagName;
389        int index;
390        String htmlId;
391       
392        while ((pathIndex < pathElements.length) && (child != null)) {
393            if ((pathElements[pathIndex] != null) && (!"".equals(pathElements[pathIndex]))) {           
394                Matcher matcher = htmlElementPattern.matcher(pathElements[pathIndex]);
395                if (!matcher.matches()) {
396                    throw new SAXException
397                        ("could not parse target DOM path element " + pathElements[pathIndex]);
398                }
399
400                tagName = matcher.group(1);
401                String indexStr = matcher.group(3);
402                htmlId = matcher.group(4);
403
404                index = -1;
405                if ((indexStr != null) && (!"".equals(indexStr))) {
406                    index = Integer.parseInt(indexStr);
407                }
408
409                compareSpec = new HTMLPageElementSpec
410                    ((HTMLDocumentSpec) document.getSpecification(), tagName, htmlId, index);
411
412                List<IGUIElement> children = model.getChildren(child);
413                child = null;
414
415                for (IGUIElement candidate : children) {
416                    if (compareSpec.getSimilarity(candidate.getSpecification())) {
417                        child = candidate;
418                        break;
419                    }
420                }
421            }
422           
423            pathIndex++;
424        }
425       
426        if (child != null) {
427            return super.getGUIElementTree().find(child);
428        }
429        else {
430            return null;
431        }
432    }
433
434    /**
435     * <p>
436     * checks if tags with the provided name must be handled in the GUI model. As an example,
437     * it is not necessary to handle "head" tags and anything included in them.
438     * </p>
439     *
440     * @param tagName the tag name to check
441     *
442     * @return true, if the tag must be considered, false else
443     */
444    private boolean tagNameMustBeConsidered(String tagName) {
445        if (!tagName.startsWith("input_")) {
446            for (int i = 0; i < tagName.length(); i++) {
447                // all known HTML tags are either letters or digits, but nothing else. Any GUI model
448                // containing something different is proprietary and, therefore, ignored.
449                if (!Character.isLetterOrDigit(tagName.charAt(i))) {
450                    return false;
451                }
452            }
453        }
454       
455        return
456            !"head".equals(tagName) && !"title".equals(tagName) && !"script".equals(tagName) &&
457            !"style".equals(tagName) && !"link".equals(tagName) && !"meta".equals(tagName) &&
458            !"iframe".equals(tagName) && !"input_hidden".equals(tagName) &&
459            !"option".equals(tagName) && !"tt".equals(tagName) && !"br".equals(tagName) &&
460            !"colgroup".equals(tagName) && !"col".equals(tagName) && !"hr".equals(tagName) &&
461            !"param".equals(tagName) && !"sfmsg".equals(tagName);
462
463    }
464
465    /**
466     * <p>
467     * checks if the children of a specified parent must be added to the GUI model or not.
468     * </p>
469     *
470     * @param parent the parent tag to check
471     *
472     * @return true, if the child of the tag must be considered, false else
473     */
474    private boolean childrenMustBeConsidered(HTMLGUIElement parent) {
475        if (parent instanceof HTMLPageElement) {
476            return !"svg".equals(((HTMLPageElement) parent).getTagName());
477        }
478        else {
479            return true;
480        }
481    }
482
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    }
856}
Note: See TracBrowser for help on using the repository browser.