669a9bd1fd542c0d6bf94afeb4061fe2d417534d
[mir.git] / source / mir / util / xml / html / HTMLParser.java
1 package mir.util.xml.html;
2
3 import java.io.*;
4 import java.util.*;
5
6 /**
7  *
8  * TODO
9  *   [x] selfclosing <br/> tags
10  *   [ ] de-html-escaping of cdata, parameter values etc
11  *   [ ] Smarter corrections
12  *       [ ]
13  *   [ ] case sensitivity optional
14  */
15
16 public class HTMLParser {
17   private HTMLSchemaInformation schemaInformation;
18
19   public HTMLParser() {
20     schemaInformation = new HTMLSchemaInformation();
21   }
22
23   public void parse(Reader aReader, ParserReceiver aReceiver) throws HTMLParserExc, HTMLParserFailure, IOException {
24     HTMLScanner scanner;
25     CoreParser parser;
26     parser = new CoreParser(aReceiver);
27     scanner = new HTMLScanner(parser, aReader);
28     scanner.run();
29   }
30
31   private class CoreParser implements HTMLScanner.ScannerReceiver {
32     private ParserReceiver receiver;
33     private Stack tagStack;
34
35     public CoreParser(ParserReceiver aReceiver) {
36       receiver = aReceiver;
37       tagStack = new Stack();
38     }
39
40     public void handleDTD(String aDTD) throws HTMLParserExc  {
41       receiver.dtd(aDTD);
42     }
43
44     public void handleOpenTag(String aTag, Map anAttributes) throws HTMLParserExc  {
45       String lowercaseTag = aTag.toLowerCase();
46
47       HTMLSchemaInformation.HTMLTagInformation tagInformation =
48           schemaInformation.lookupTag(lowercaseTag);
49
50       Map attributes = new HashMap();
51
52       Iterator i = anAttributes.entrySet().iterator();
53       while (i.hasNext()) {
54         Map.Entry entry = (Map.Entry) i.next();
55
56         attributes.put(((String) entry.getKey()).toLowerCase(), entry.getValue());
57       }
58
59       if (tagInformation!=null) {
60         if (tagInformation.getIsBlock()) {
61           closeAllInlineTags();
62         }
63
64         closeAllAutoclosingTags(tagInformation);
65
66         receiver.openTag(lowercaseTag, attributes);
67         if (tagInformation.getHasBody()) {
68           tagStack.push(lowercaseTag);
69         }
70         else {
71           receiver.closeTag(lowercaseTag);
72         }
73       }
74       else {
75         receiver.openTag(lowercaseTag, attributes);
76         tagStack.push(lowercaseTag);
77       }
78     }
79
80     public void handleClosingTag(String aTag) throws HTMLParserExc {
81       String lowercaseTag = aTag.toLowerCase();
82
83       HTMLSchemaInformation.HTMLTagInformation tagInformation =
84           schemaInformation.lookupTag(lowercaseTag);
85
86       if (tagInformation!=null) {
87         if (tagInformation.getIsBlock()) {
88           closeAllInlineTags();
89         }
90       }
91
92       int index = tagStack.search(lowercaseTag);
93
94       if (index>-1 && index<4) {
95         for (int i=0; i<index; i++) {
96           closeUpmostTag();
97         }
98       }
99     }
100
101     public void handleCData(String aData)  throws HTMLParserExc {
102       receiver.cdata(aData);
103     }
104
105     public void handleComment(String aTag) throws HTMLParserExc  {
106       receiver.comment(aTag);
107     }
108
109     public void handleEndOfStream() throws HTMLParserExc {
110       while (!tagStack.empty())
111         closeUpmostTag();
112     }
113
114     private void closeAllAutoclosingTags(HTMLSchemaInformation.HTMLTagInformation aTagInformation) throws HTMLParserExc {
115       while (!tagStack.empty()) {
116         String tag = (String) tagStack.peek();
117
118         if (aTagInformation.autoClose(tag)) {
119           closeUpmostTag();
120         }
121         else {
122           break;
123         }
124       }
125     }
126
127     private void closeAllInlineTags() throws HTMLParserExc {
128       while (!tagStack.empty()) {
129         HTMLSchemaInformation.HTMLTagInformation tagInformation =
130             schemaInformation.lookupTag((String) tagStack.peek());
131
132         if (tagInformation!=null && !tagInformation.getIsBlock()) {
133           closeUpmostTag();
134         }
135         else {
136           break;
137         }
138       }
139     }
140
141     private void closeUpmostTag() throws HTMLParserExc {
142       receiver.closeTag((String) tagStack.peek());
143       tagStack.pop();
144     }
145   }
146
147   public interface ParserReceiver {
148     public void dtd(String aDTD) throws HTMLParserExc;
149     public void openTag(String aTag, Map anAttributes) throws HTMLParserExc;
150     public void closeTag(String aTag) throws HTMLParserExc;
151     public void comment(String aData) throws HTMLParserExc;
152     public void cdata(String aData) throws HTMLParserExc;
153   }
154 }