1. /*
  2. * @(#)ContentModelState.java 1.7 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. /**
  12. * A content model state. This is basically a list of pointers to
  13. * the BNF expression representing the model (the ContentModel).
  14. * Each element in a DTD has a content model which describes the
  15. * elements that may occur inside, and the order in which they can
  16. * occur.
  17. * <p>
  18. * Each time a token is reduced a new state is created.
  19. * <p>
  20. * See Annex H on page 556 of the SGML handbook for more information.
  21. *
  22. * @see Parser
  23. * @see DTD
  24. * @see Element
  25. * @see ContentModel
  26. * @author Arthur van Hoff
  27. * @version 1.7 02/02/00
  28. */
  29. class ContentModelState {
  30. ContentModel model;
  31. long value;
  32. ContentModelState next;
  33. /**
  34. * Create a content model state for a content model.
  35. */
  36. public ContentModelState(ContentModel model) {
  37. this(model, null, 0);
  38. }
  39. /**
  40. * Create a content model state for a content model given the
  41. * remaining state that needs to be reduce.
  42. */
  43. ContentModelState(Object content, ContentModelState next) {
  44. this(content, next, 0);
  45. }
  46. /**
  47. * Create a content model state for a content model given the
  48. * remaining state that needs to be reduce.
  49. */
  50. ContentModelState(Object content, ContentModelState next, long value) {
  51. this.model = (ContentModel)content;
  52. this.next = next;
  53. this.value = value;
  54. }
  55. /**
  56. * Return the content model that is relevant to the current state.
  57. */
  58. public ContentModel getModel() {
  59. ContentModel m = model;
  60. for (int i = 0; i < value; i++) {
  61. if (m.next != null) {
  62. m = m.next;
  63. } else {
  64. return null;
  65. }
  66. }
  67. return m;
  68. }
  69. /**
  70. * Check if the state can be terminated. That is there are no more
  71. * tokens required in the input stream.
  72. * @return true if the model can terminate without further input
  73. */
  74. public boolean terminate() {
  75. switch (model.type) {
  76. case '+':
  77. if ((value == 0) && !(model).empty()) {
  78. return false;
  79. }
  80. case '*':
  81. case '?':
  82. return (next == null) || next.terminate();
  83. case '|':
  84. for (ContentModel m = (ContentModel)model.content ; m != null ; m = m.next) {
  85. if (m.empty()) {
  86. return (next == null) || next.terminate();
  87. }
  88. }
  89. return false;
  90. case '&': {
  91. ContentModel m = (ContentModel)model.content;
  92. for (int i = 0 ; m != null ; i++, m = m.next) {
  93. if ((value & (1L << i)) == 0) {
  94. if (!m.empty()) {
  95. return false;
  96. }
  97. }
  98. }
  99. return (next == null) || next.terminate();
  100. }
  101. case ',': {
  102. ContentModel m = (ContentModel)model.content;
  103. for (int i = 0 ; i < value ; i++, m = m.next);
  104. for (; (m != null) && m.empty() ; m = m.next);
  105. if (m != null) {
  106. return false;
  107. }
  108. return (next == null) || next.terminate();
  109. }
  110. default:
  111. return false;
  112. }
  113. }
  114. /**
  115. * Check if the state can be terminated. That is there are no more
  116. * tokens required in the input stream.
  117. * @return the only possible element that can occur next
  118. */
  119. public Element first() {
  120. switch (model.type) {
  121. case '*':
  122. case '?':
  123. case '|':
  124. case '&':
  125. return null;
  126. case '+':
  127. return model.first();
  128. case ',': {
  129. ContentModel m = (ContentModel)model.content;
  130. for (int i = 0 ; i < value ; i++, m = m.next);
  131. return m.first();
  132. }
  133. default:
  134. return model.first();
  135. }
  136. }
  137. /**
  138. * Advance this state to a new state. An exception is thrown if the
  139. * token is illegal at this point in the content model.
  140. * @return next state after reducing a token
  141. */
  142. public ContentModelState advance(Object token) {
  143. switch (model.type) {
  144. case '+':
  145. if (model.first(token)) {
  146. return new ContentModelState(model.content,
  147. new ContentModelState(model, next, value + 1)).advance(token);
  148. }
  149. if (value != 0) {
  150. if (next != null) {
  151. return next.advance(token);
  152. } else {
  153. return null;
  154. }
  155. }
  156. break;
  157. case '*':
  158. if (model.first(token)) {
  159. return new ContentModelState(model.content, this).advance(token);
  160. }
  161. if (next != null) {
  162. return next.advance(token);
  163. } else {
  164. return null;
  165. }
  166. case '?':
  167. if (model.first(token)) {
  168. return new ContentModelState(model.content, next).advance(token);
  169. }
  170. if (next != null) {
  171. return next.advance(token);
  172. } else {
  173. return null;
  174. }
  175. case '|':
  176. for (ContentModel m = (ContentModel)model.content ; m != null ; m = m.next) {
  177. if (m.first(token)) {
  178. return new ContentModelState(m, next).advance(token);
  179. }
  180. }
  181. break;
  182. case ',': {
  183. ContentModel m = (ContentModel)model.content;
  184. for (int i = 0 ; i < value ; i++, m = m.next);
  185. if (m.first(token) || m.empty()) {
  186. if (m.next == null) {
  187. return new ContentModelState(m, next).advance(token);
  188. } else {
  189. return new ContentModelState(m,
  190. new ContentModelState(model, next, value + 1)).advance(token);
  191. }
  192. }
  193. break;
  194. }
  195. case '&': {
  196. ContentModel m = (ContentModel)model.content;
  197. boolean complete = true;
  198. for (int i = 0 ; m != null ; i++, m = m.next) {
  199. if ((value & (1L << i)) == 0) {
  200. if (m.first(token)) {
  201. return new ContentModelState(m,
  202. new ContentModelState(model, next, value | (1L << i))).advance(token);
  203. }
  204. if (!m.empty()) {
  205. complete = false;
  206. }
  207. }
  208. }
  209. if (complete) {
  210. if (next != null) {
  211. return next.advance(token);
  212. } else {
  213. return null;
  214. }
  215. }
  216. break;
  217. }
  218. default:
  219. if (model.content == token) {
  220. return next;
  221. }
  222. }
  223. // We used to throw this exception at this point. However, it
  224. // was determined that throwing this exception was more expensive
  225. // than returning null, and we could not justify to ourselves why
  226. // it was necessary to throw an exception, rather than simply
  227. // returning null. I'm leaving it in a commented out state so
  228. // that it can be easily restored if the situation ever arises.
  229. //
  230. // throw new IllegalArgumentException("invalid token: " + token);
  231. return null;
  232. }
  233. }