1. /*
  2. * @(#)ContentModel.java 1.11 04/05/05
  3. *
  4. * Copyright 2004 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 java.util.Vector;
  9. import java.util.Enumeration;
  10. import java.io.*;
  11. /**
  12. * A representation of a content model. A content model is
  13. * basically a restricted BNF expression. It is restricted in
  14. * the sense that it must be deterministic. This means that you
  15. * don't have to represent it as a finite state automata.<p>
  16. * See Annex H on page 556 of the SGML handbook for more information.
  17. *
  18. * @author Arthur van Hoff
  19. * @version 1.11,05/05/04
  20. *
  21. */
  22. public final class ContentModel implements Serializable {
  23. /**
  24. * Type. Either '*', '?', '+', ',', '|', '&'.
  25. */
  26. public int type;
  27. /**
  28. * The content. Either an Element or a ContentModel.
  29. */
  30. public Object content;
  31. /**
  32. * The next content model (in a ',', '|' or '&' expression).
  33. */
  34. public ContentModel next;
  35. public ContentModel() {
  36. }
  37. /**
  38. * Create a content model for an element.
  39. */
  40. public ContentModel(Element content) {
  41. this(0, content, null);
  42. }
  43. /**
  44. * Create a content model of a particular type.
  45. */
  46. public ContentModel(int type, ContentModel content) {
  47. this(type, content, null);
  48. }
  49. /**
  50. * Create a content model of a particular type.
  51. */
  52. public ContentModel(int type, Object content, ContentModel next) {
  53. this.type = type;
  54. this.content = content;
  55. this.next = next;
  56. }
  57. /**
  58. * Return true if the content model could
  59. * match an empty input stream.
  60. */
  61. public boolean empty() {
  62. switch (type) {
  63. case '*':
  64. case '?':
  65. return true;
  66. case '+':
  67. case '|':
  68. for (ContentModel m = (ContentModel)content ; m != null ; m = m.next) {
  69. if (m.empty()) {
  70. return true;
  71. }
  72. }
  73. return false;
  74. case ',':
  75. case '&':
  76. for (ContentModel m = (ContentModel)content ; m != null ; m = m.next) {
  77. if (!m.empty()) {
  78. return false;
  79. }
  80. }
  81. return true;
  82. default:
  83. return false;
  84. }
  85. }
  86. /**
  87. * Update elemVec with the list of elements that are
  88. * part of the this contentModel.
  89. */
  90. public void getElements(Vector<Element> elemVec) {
  91. switch (type) {
  92. case '*':
  93. case '?':
  94. case '+':
  95. ((ContentModel)content).getElements(elemVec);
  96. break;
  97. case ',':
  98. case '|':
  99. case '&':
  100. for (ContentModel m=(ContentModel)content; m != null; m=m.next){
  101. m.getElements(elemVec);
  102. }
  103. break;
  104. default:
  105. elemVec.addElement((Element)content);
  106. }
  107. }
  108. private boolean valSet[];
  109. private boolean val[];
  110. // A cache used by first(). This cache was found to speed parsing
  111. // by about 10% (based on measurements of the 4-12 code base after
  112. // buffering was fixed).
  113. /**
  114. * Return true if the token could potentially be the
  115. * first token in the input stream.
  116. */
  117. public boolean first(Object token) {
  118. switch (type) {
  119. case '*':
  120. case '?':
  121. case '+':
  122. return ((ContentModel)content).first(token);
  123. case ',':
  124. for (ContentModel m = (ContentModel)content ; m != null ; m = m.next) {
  125. if (m.first(token)) {
  126. return true;
  127. }
  128. if (!m.empty()) {
  129. return false;
  130. }
  131. }
  132. return false;
  133. case '|':
  134. case '&': {
  135. Element e = (Element) token;
  136. if (valSet == null) {
  137. valSet = new boolean[Element.maxIndex + 1];
  138. val = new boolean[Element.maxIndex + 1];
  139. // All Element instances are created before this ever executes
  140. }
  141. if (valSet[e.index]) {
  142. return val[e.index];
  143. }
  144. for (ContentModel m = (ContentModel)content ; m != null ; m = m.next) {
  145. if (m.first(token)) {
  146. val[e.index] = true;
  147. break;
  148. }
  149. }
  150. valSet[e.index] = true;
  151. return val[e.index];
  152. }
  153. default:
  154. return (content == token);
  155. // PENDING: refer to comment in ContentModelState
  156. /*
  157. if (content == token) {
  158. return true;
  159. }
  160. Element e = (Element)content;
  161. if (e.omitStart() && e.content != null) {
  162. return e.content.first(token);
  163. }
  164. return false;
  165. */
  166. }
  167. }
  168. /**
  169. * Return the element that must be next.
  170. */
  171. public Element first() {
  172. switch (type) {
  173. case '&':
  174. case '|':
  175. case '*':
  176. case '?':
  177. return null;
  178. case '+':
  179. case ',':
  180. return ((ContentModel)content).first();
  181. default:
  182. return (Element)content;
  183. }
  184. }
  185. /**
  186. * Convert to a string.
  187. */
  188. public String toString() {
  189. switch (type) {
  190. case '*':
  191. return content + "*";
  192. case '?':
  193. return content + "?";
  194. case '+':
  195. return content + "+";
  196. case ',':
  197. case '|':
  198. case '&':
  199. char data[] = {' ', (char)type, ' '};
  200. String str = "";
  201. for (ContentModel m = (ContentModel)content ; m != null ; m = m.next) {
  202. str = str + m;
  203. if (m.next != null) {
  204. str += new String(data);
  205. }
  206. }
  207. return "(" + str + ")";
  208. default:
  209. return content.toString();
  210. }
  211. }
  212. }