1. /*
  2. * @(#)DocumentParser.java 1.24 03/01/23
  3. *
  4. * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.swing.text.html.parser;
  8. import javax.swing.text.SimpleAttributeSet;
  9. import javax.swing.text.html.HTMLEditorKit;
  10. import javax.swing.text.html.HTML;
  11. import javax.swing.text.ChangedCharSetException;
  12. import java.util.*;
  13. import java.io.*;
  14. import java.net.*;
  15. import sun.io.*;
  16. /**
  17. * A Parser for HTML Documents (actually, you can specify a DTD, but
  18. * you should really only use this class with the html dtd in swing).
  19. * Reads an InputStream of HTML and
  20. * invokes the appropriate methods in the ParserCallback class. This
  21. * is the default parser used by HTMLEditorKit to parse HTML url's.
  22. * <p>This will message the callback for all valid tags, as well as
  23. * tags that are implied but not explicitly specified. For example, the
  24. * html string (<p>blah) only has a p tag defined. The callback
  25. * will see the following methods:
  26. * <ol><li><i>handleStartTag(html, ...)</i></li>
  27. * <li><i>handleStartTag(head, ...)</i></li>
  28. * <li><i>handleEndTag(head)</i></li>
  29. * <li><i>handleStartTag(body, ...)</i></li>
  30. * <li>handleStartTag(p, ...)</i></li>
  31. * <li>handleText(...)</li>
  32. * <li><i>handleEndTag(p)</i></li>
  33. * <li><i>handleEndTag(body)</i></li>
  34. * <li><i>handleEndTag(html)</i></li>
  35. * </ol>
  36. * The items in <i>italic</i> are implied, that is, although they were not
  37. * explicitly specified, to be correct html they should have been present
  38. * (head isn't necessary, but it is still generated). For tags that
  39. * are implied, the AttributeSet argument will have a value of
  40. * <code>Boolean.TRUE</code> for the key
  41. * <code>HTMLEditorKit.ParserCallback.IMPLIED</code>.
  42. * <p>HTML.Attributes defines a type safe enumeration of html attributes.
  43. * If an attribute key of a tag is defined in HTML.Attribute, the
  44. * HTML.Attribute will be used as the key, otherwise a String will be used.
  45. * For example <p foo=bar class=neat> has two attributes. foo is
  46. * not defined in HTML.Attribute, where as class is, therefore the
  47. * AttributeSet will have two values in it, HTML.Attribute.CLASS with
  48. * a String value of 'neat' and the String key 'foo' with a String value of
  49. * 'bar'.
  50. * <p>The position argument will indicate the start of the tag, comment
  51. * or text. Similiar to arrays, the first character in the stream has a
  52. * position of 0. For tags that are
  53. * implied the position will indicate
  54. * the location of the next encountered tag. In the first example,
  55. * the implied start body and html tags will have the same position as the
  56. * p tag, and the implied end p, html and body tags will all have the same
  57. * position.
  58. * <p>As html skips whitespace the position for text will be the position
  59. * of the first valid character, eg in the string '\n\n\nblah'
  60. * the text 'blah' will have a position of 3, the newlines are skipped.
  61. * <p>
  62. * For attributes that do not have a value, eg in the html
  63. * string <code><foo blah></code> the attribute <code>blah</code>
  64. * does not have a value, there are two possible values that will be
  65. * placed in the AttributeSet's value:
  66. * <ul>
  67. * <li>If the DTD does not contain an definition for the element, or the
  68. * definition does not have an explicit value then the value in the
  69. * AttributeSet will be <code>HTML.NULL_ATTRIBUTE_VALUE</code>.
  70. * <li>If the DTD contains an explicit value, as in:
  71. * <code><!ATTLIST OPTION selected (selected) #IMPLIED></code>
  72. * this value from the dtd (in this case selected) will be used.
  73. * </ul>
  74. * <p>
  75. * Once the stream has been parsed, the callback is notified of the most
  76. * likely end of line string. The end of line string will be one of
  77. * \n, \r or \r\n, which ever is encountered the most in parsing the
  78. * stream.
  79. *
  80. * @version 1.24 01/23/03
  81. * @author Sunita Mani
  82. */
  83. public class DocumentParser extends javax.swing.text.html.parser.Parser {
  84. private int inbody;
  85. private int intitle;
  86. private int inhead;
  87. private int instyle;
  88. private boolean seentitle;
  89. private HTMLEditorKit.ParserCallback callback = null;
  90. private boolean ignoreCharSet = false;
  91. private static final boolean debugFlag = false;
  92. public DocumentParser(DTD dtd) {
  93. super(dtd);
  94. }
  95. public void parse(Reader in, HTMLEditorKit.ParserCallback callback, boolean ignoreCharSet) throws IOException {
  96. this.ignoreCharSet = ignoreCharSet;
  97. this.callback = callback;
  98. parse(in);
  99. // end of line
  100. callback.handleEndOfLineString(getEndOfLineString());
  101. }
  102. /**
  103. * Handle Start Tag.
  104. */
  105. protected void handleStartTag(TagElement tag) {
  106. Element elem = tag.getElement();
  107. if (elem == dtd.body) {
  108. inbody++;
  109. } else if (elem == dtd.html) {
  110. } else if (elem == dtd.head) {
  111. inhead++;
  112. } else if (elem == dtd.title) {
  113. intitle++;
  114. } else if (elem == dtd.style) {
  115. instyle++;
  116. }
  117. if (debugFlag) {
  118. if (tag.fictional()) {
  119. debug("Start Tag: " + tag.getHTMLTag() + " pos: " + getCurrentPos());
  120. } else {
  121. debug("Start Tag: " + tag.getHTMLTag() + " attributes: " +
  122. getAttributes() + " pos: " + getCurrentPos());
  123. }
  124. }
  125. if (tag.fictional()) {
  126. SimpleAttributeSet attrs = new SimpleAttributeSet();
  127. attrs.addAttribute(HTMLEditorKit.ParserCallback.IMPLIED,
  128. Boolean.TRUE);
  129. callback.handleStartTag(tag.getHTMLTag(), attrs,
  130. getBlockStartPosition());
  131. } else {
  132. callback.handleStartTag(tag.getHTMLTag(), getAttributes(),
  133. getBlockStartPosition());
  134. flushAttributes();
  135. }
  136. }
  137. protected void handleComment(char text[]) {
  138. if (debugFlag) {
  139. debug("comment: ->" + new String(text) + "<-"
  140. + " pos: " + getCurrentPos());
  141. }
  142. callback.handleComment(text, getBlockStartPosition());
  143. }
  144. /**
  145. * Handle Empty Tag.
  146. */
  147. protected void handleEmptyTag(TagElement tag) throws ChangedCharSetException {
  148. Element elem = tag.getElement();
  149. if (elem == dtd.meta && !ignoreCharSet) {
  150. SimpleAttributeSet atts = getAttributes();
  151. if (atts != null) {
  152. String content = (String)atts.getAttribute(HTML.Attribute.CONTENT);
  153. if (content != null) {
  154. if ("content-type".equalsIgnoreCase((String)atts.getAttribute(HTML.Attribute.HTTPEQUIV))) {
  155. throw new ChangedCharSetException(content, false);
  156. } else if ("charset" .equalsIgnoreCase((String)atts.getAttribute(HTML.Attribute.HTTPEQUIV))) {
  157. throw new ChangedCharSetException(content, true);
  158. }
  159. }
  160. }
  161. }
  162. if (inbody != 0 || elem == dtd.meta || elem == dtd.base || elem == dtd.isindex || elem == dtd.style || elem == dtd.link) {
  163. if (debugFlag) {
  164. if (tag.fictional()) {
  165. debug("Empty Tag: " + tag.getHTMLTag() + " pos: " + getCurrentPos());
  166. } else {
  167. debug("Empty Tag: " + tag.getHTMLTag() + " attributes: "
  168. + getAttributes() + " pos: " + getCurrentPos());
  169. }
  170. }
  171. if (tag.fictional()) {
  172. SimpleAttributeSet attrs = new SimpleAttributeSet();
  173. attrs.addAttribute(HTMLEditorKit.ParserCallback.IMPLIED,
  174. Boolean.TRUE);
  175. callback.handleSimpleTag(tag.getHTMLTag(), attrs,
  176. getBlockStartPosition());
  177. } else {
  178. callback.handleSimpleTag(tag.getHTMLTag(), getAttributes(),
  179. getBlockStartPosition());
  180. flushAttributes();
  181. }
  182. }
  183. }
  184. /**
  185. * Handle End Tag.
  186. */
  187. protected void handleEndTag(TagElement tag) {
  188. Element elem = tag.getElement();
  189. if (elem == dtd.body) {
  190. inbody--;
  191. } else if (elem == dtd.title) {
  192. intitle--;
  193. seentitle = true;
  194. } else if (elem == dtd.head) {
  195. inhead--;
  196. } else if (elem == dtd.style) {
  197. instyle--;
  198. }
  199. if (debugFlag) {
  200. debug("End Tag: " + tag.getHTMLTag() + " pos: " + getCurrentPos());
  201. }
  202. callback.handleEndTag(tag.getHTMLTag(), getBlockStartPosition());
  203. }
  204. /**
  205. * Handle Text.
  206. */
  207. protected void handleText(char data[]) {
  208. if (data != null) {
  209. if (inbody != 0 || ((instyle != 0) ||
  210. ((intitle != 0) && !seentitle))) {
  211. if (debugFlag) {
  212. debug("text: ->" + new String(data) + "<-" + " pos: " + getCurrentPos());
  213. }
  214. callback.handleText(data, getBlockStartPosition());
  215. }
  216. }
  217. }
  218. /*
  219. * Error handling.
  220. */
  221. protected void handleError(int ln, String errorMsg) {
  222. if (debugFlag) {
  223. debug("Error: ->" + errorMsg + "<-" + " pos: " + getCurrentPos());
  224. }
  225. /* PENDING: need to improve the error string. */
  226. callback.handleError(errorMsg, getCurrentPos());
  227. }
  228. /*
  229. * debug messages
  230. */
  231. private void debug(String msg) {
  232. System.out.println(msg);
  233. }
  234. }