1. /*
  2. * @(#)ContentModel.java 1.5 01/11/29
  3. *
  4. * Copyright 2002 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.5,11/29/01
  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 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(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. }
  156. }
  157. /**
  158. * Return the element that must be next.
  159. */
  160. public Element first() {
  161. switch (type) {
  162. case '&':
  163. case '|':
  164. case '*':
  165. case '?':
  166. return null;
  167. case '+':
  168. case ',':
  169. return ((ContentModel)content).first();
  170. default:
  171. return (Element)content;
  172. }
  173. }
  174. /**
  175. * Convert to a string.
  176. */
  177. public String toString() {
  178. switch (type) {
  179. case '*':
  180. return content + "*";
  181. case '?':
  182. return content + "?";
  183. case '+':
  184. return content + "+";
  185. case ',':
  186. case '|':
  187. case '&':
  188. char data[] = {' ', (char)type, ' '};
  189. String str = "";
  190. for (ContentModel m = (ContentModel)content ; m != null ; m = m.next) {
  191. str = str + m;
  192. if (m.next != null) {
  193. str += new String(data);
  194. }
  195. }
  196. return "(" + str + ")";
  197. default:
  198. return content.toString();
  199. }
  200. }
  201. }