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