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

Last change on this file since 1876 was 1817, checked in by pharms, 10 years ago
  • extended GUI Mapping for SWE website
  • 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            !"wappalyzerdata".equals(tagName);
463
464    }
465
466    /**
467     * <p>
468     * checks if the children of a specified parent must be added to the GUI model or not.
469     * </p>
470     *
471     * @param parent the parent tag to check
472     *
473     * @return true, if the child of the tag must be considered, false else
474     */
475    private boolean childrenMustBeConsidered(HTMLGUIElement parent) {
476        if (parent instanceof HTMLPageElement) {
477            return !"svg".equals(((HTMLPageElement) parent).getTagName());
478        }
479        else {
480            return true;
481        }
482    }
483
484    /**
485     * <p>specification for a replacement consisting of path of tag or document specifications
486     * and the appropriate replacement.</p>
487     */
488    private static class ReplacementSpecification {
489       
490        /**
491         * <p>
492         * the pattern used for parsing parsing parameters
493         * </p>
494         */
495        private Pattern htmlElementSpecPattern = Pattern.compile
496            ("(document\\(path=([\\w/-]+)\\))|((\\w+)(\\[(\\d+)\\]|\\(htmlId=([\\w-_#]+)\\))?)");
497       
498        /**
499         * <p>
500         * the path of specifications (tags and document) specifying the tag for which this
501         * replacement is specified
502         * </p>
503         */
504        private List<Spec> specs = new LinkedList<Spec>();
505       
506        /**
507         * <p>
508         * the name of the last tag in the specification path (used for indexing purposes)
509         * </p>
510         */
511        private String lastTagName;
512       
513        /**
514         * <p>
515         * the configured replacement
516         * </p>
517         */
518        private String replacement;
519
520        /**
521         * <p>
522         * initializes the specification with the key/value strings from the config file. Parses
523         * the key to get the specification path consisting of, optionally, a document
524         * specification and one or more tag specification.
525         * </p>
526         */
527        public ReplacementSpecification(String tagSpec, String replacement) {
528            List<String> tagSpecs = split(tagSpec);
529           
530            for (int i = 0; i < tagSpecs.size(); i++) {
531                Matcher matcher = htmlElementSpecPattern.matcher(tagSpecs.get(i));
532               
533                if (!matcher.matches()) {
534                    throw new IllegalArgumentException
535                        ("illegal tag specification " + tagSpecs.get(i));
536                }
537               
538                if (matcher.group(1) != null) {
539                    this.specs.add(new DocumentSpec(matcher.group(2)));
540                }
541                else if (matcher.group(4) != null) {
542                    String indexConditionStr = matcher.group(6);
543                    Integer indexCondition = null;
544               
545                    if (indexConditionStr != null) {
546                        try {
547                            indexCondition = Integer.parseInt(indexConditionStr);
548                        }
549                        catch (NumberFormatException e) {
550                            throw new IllegalArgumentException
551                                ("illegal tag index specification " + indexConditionStr, e);
552                        }
553                    }
554               
555                    this.specs.add
556                        (new TagSpec(matcher.group(4), indexCondition, matcher.group(7)));
557                }
558            }
559           
560            this.lastTagName = ((TagSpec) this.specs.get(this.specs.size() - 1)).getTagName();
561           
562            this.replacement = replacement;
563        }
564
565        /**
566         * <p>
567         * convenience method to split the key of a key/value pair from the config file into its
568         * parts
569         * </p>
570         */
571        private List<String> split(String tagSpec) {
572            List<String> specs = new LinkedList<String>();
573           
574            StringBuffer currentSpec = new StringBuffer();
575            int openBraces = 0;
576           
577            for (int i = 0; i < tagSpec.length(); i++) {
578                char curChar = tagSpec.charAt(i);
579                if ((openBraces == 0) && ('/' == curChar) && (currentSpec.length() > 0)) {
580                    specs.add(currentSpec.toString());
581                    currentSpec.setLength(0);
582                }
583                else {
584                    if ('(' == curChar) {
585                        openBraces++;
586                    }
587                    else if (')' == curChar) {
588                        openBraces--;
589                    }
590                    currentSpec.append(curChar);
591                }
592            }
593           
594            if (currentSpec.length() > 0) {
595                specs.add(currentSpec.toString());
596            }
597           
598            return specs;
599        }
600
601        /**
602         * <p>
603         * checks, if the tag identified by the parameters matches this specificaiton.
604         * </p>
605         */
606        private boolean matches(String tagName, int index, String htmlId, HTMLGUIElement parent) {
607            String currentTagName = tagName;
608            int currentIndex = index;
609            String currentHtmlId = htmlId;
610            String currentPath = null;
611            HTMLGUIElement currentParent = parent;
612           
613            int i = specs.size() - 1;
614           
615            while (i >= 0) {
616                if ((specs.get(i) instanceof TagSpec) &&
617                    (!((TagSpec) specs.get(i)).matches(currentTagName, currentIndex, currentHtmlId)))
618                {
619                    return false;
620                }
621                else if ((specs.get(i) instanceof DocumentSpec) &&
622                         (!((DocumentSpec) specs.get(i)).matches(currentPath)))
623                {
624                    return false;
625                }
626               
627                i--;
628               
629                if (i >= 0) {
630                    if (currentParent instanceof HTMLPageElement) {
631                        currentTagName = ((HTMLPageElement) currentParent).getTagName();
632                        currentIndex = ((HTMLPageElement) currentParent).getIndex();
633                        currentHtmlId = ((HTMLPageElement) currentParent).getHtmlId();
634                        currentPath = null;
635                        currentParent = (HTMLGUIElement) currentParent.getParent();
636                     }
637                    else if (currentParent instanceof HTMLDocument) {
638                        currentTagName = null;
639                        currentIndex = Integer.MIN_VALUE;
640                        currentHtmlId = null;
641                        currentPath = ((HTMLDocument) currentParent).getPath();
642                        currentParent = (HTMLGUIElement) currentParent.getParent();
643                    }
644                    else {
645                        throw new IllegalArgumentException
646                            ("specification matches documents or servers. This is not supported yet.");
647                    }
648                }
649            }
650           
651            return true;
652        }
653
654        /**
655         * <p>
656         * returns the specified replacement
657         * </p>
658         */
659        private String getReplacement() {
660            return replacement;
661        }
662
663        /**
664         * <p>
665         * returns the name of the last tag specified in the specification path
666         * </p>
667         */
668        private String getLastTagName() {
669            return lastTagName;
670        }
671
672        /* (non-Javadoc)
673         * @see java.lang.Object#toString()
674         */
675        @Override
676        public String toString() {
677            StringBuffer result = new StringBuffer();
678            for (Spec spec : specs) {
679                if (result.length() > 0) {
680                    result.append("/");
681                }
682                result.append(spec);
683            }
684           
685            result.append('=');
686            result.append(replacement);
687           
688            return result.toString();
689        }
690       
691    }
692
693    /**
694     * <p>
695     * parent type for document and tag specifications
696     * </p>
697     */
698    private static interface Spec { }
699
700    /**
701     * <p>
702     * specification of a document
703     * </p>
704     */
705    private static class DocumentSpec implements Spec {
706       
707        /**
708         * <p>
709         * the part of the path the document path must have to match this specification
710         * </p>
711         */
712        private String pathPart;
713
714        /**
715         * <p>
716         * initializes the document specification with the path part
717         * </p>
718         */
719        private DocumentSpec(String pathPart) {
720            this.pathPart = pathPart;
721        }
722
723        /**
724         * <p>
725         * returns true if the provided path contains the path part provided to the parameter
726         * </p>
727         */
728        private boolean matches(String path) {
729            return path.contains(pathPart);
730        }
731
732        /* (non-Javadoc)
733         * @see java.lang.Object#toString()
734         */
735        @Override
736        public String toString() {
737            return "document(path=" + pathPart + ")";
738        }
739    }
740
741    /**
742     * <p>
743     * specification for a tag containing a tag name and either an index or id condition.
744     * </p>
745     */
746    private static class TagSpec implements Spec {
747
748        /**
749         * <p>
750         * the name of the tag to match
751         * </p>
752         */
753        private String tagName;
754       
755        /**
756         * <p>
757         * the index of the tag to match
758         * </p>
759         */
760        private Integer indexCondition;
761       
762        /**
763         * <p>
764         * the id of the tag to match
765         * </p>
766         */
767        private String idCondition;
768
769        /**
770         * <p>
771         * initializes the specification with all required parameters
772         * </p>
773         */
774        private TagSpec(String tagName, Integer indexCondition, String idCondition) {
775            this.tagName = tagName;
776            this.indexCondition = indexCondition;
777            this.idCondition = idCondition;
778        }
779
780        /**
781         * <p>
782         * returns true if the provided tag information matches this specification. The id is
783         * checked first. If the id condition has a # at some position, the respective element
784         * of the provided id is ignored.
785         * </p>
786         */
787        private boolean matches(String tagName, int index, String htmlId) {
788            if (!this.tagName.equals(tagName)) {
789                return false;
790            }
791           
792            if (idCondition != null) {
793                if (!idCondition.equals(htmlId)) {
794                    // check if the id condition would match with ignoring specific characters
795                    if ((htmlId != null) && (idCondition.indexOf('#') > -1)) {
796                        // first of all, the length must match
797                        if (idCondition.length() != htmlId.length()) {
798                            return false;
799                        }
800                       
801                        for (int i = 0; i < idCondition.length(); i++) {
802                            if ((idCondition.charAt(i) != '#') &&
803                                (idCondition.charAt(i) != htmlId.charAt(i)))
804                            {
805                                // if there is a character that is neither ignored nor matches
806                                // the condition at a specific position, return "no match"
807                                return false;
808                            }
809                        }
810                       
811                    }
812                    else {
813                        // no condition ignoring specific characters
814                        return false;
815                    }
816                }
817            }
818           
819            if ((indexCondition != null) && (index != indexCondition)) {
820                return false;
821            }
822           
823            return true;
824        }
825
826        /**
827         * <p>
828         * returns the name of the tags matched by this specification
829         * </p>
830         */
831        private String getTagName() {
832            return tagName;
833        }
834
835        /* (non-Javadoc)
836         * @see java.lang.Object#toString()
837         */
838        @Override
839        public String toString() {
840            StringBuffer result = new StringBuffer(tagName);
841           
842            if (idCondition != null) {
843                result.append("(htmlId=");
844                result.append(idCondition);
845                result.append(')');
846            }
847            else if (indexCondition != null) {
848                result.append('[');
849                result.append(indexCondition);
850                result.append(']');
851            }
852           
853            return result.toString();
854        }
855       
856    }
857}
Note: See TracBrowser for help on using the repository browser.