1 /*
2 * Copyright 1998-2004 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26 package javax.swing.text.html.parser;
27
28 import java.util.Vector;
29 import java.util.Enumeration;
30 import java.io;
31
32
33 /**
34 * A representation of a content model. A content model is
35 * basically a restricted BNF expression. It is restricted in
36 * the sense that it must be deterministic. This means that you
37 * don't have to represent it as a finite state automata.<p>
38 * See Annex H on page 556 of the SGML handbook for more information.
39 *
40 * @author Arthur van Hoff
41 *
42 */
43 public final class ContentModel implements Serializable {
44 /**
45 * Type. Either '*', '?', '+', ',', '|', '&'.
46 */
47 public int type;
48
49 /**
50 * The content. Either an Element or a ContentModel.
51 */
52 public Object content;
53
54 /**
55 * The next content model (in a ',', '|' or '&' expression).
56 */
57 public ContentModel next;
58
59 public ContentModel() {
60 }
61
62 /**
63 * Create a content model for an element.
64 */
65 public ContentModel(Element content) {
66 this(0, content, null);
67 }
68
69 /**
70 * Create a content model of a particular type.
71 */
72 public ContentModel(int type, ContentModel content) {
73 this(type, content, null);
74 }
75
76 /**
77 * Create a content model of a particular type.
78 */
79 public ContentModel(int type, Object content, ContentModel next) {
80 this.type = type;
81 this.content = content;
82 this.next = next;
83 }
84
85 /**
86 * Return true if the content model could
87 * match an empty input stream.
88 */
89 public boolean empty() {
90 switch (type) {
91 case '*':
92 case '?':
93 return true;
94
95 case '+':
96 case '|':
97 for (ContentModel m = (ContentModel)content ; m != null ; m = m.next) {
98 if (m.empty()) {
99 return true;
100 }
101 }
102 return false;
103
104 case ',':
105 case '&':
106 for (ContentModel m = (ContentModel)content ; m != null ; m = m.next) {
107 if (!m.empty()) {
108 return false;
109 }
110 }
111 return true;
112
113 default:
114 return false;
115 }
116 }
117
118 /**
119 * Update elemVec with the list of elements that are
120 * part of the this contentModel.
121 */
122 public void getElements(Vector<Element> elemVec) {
123 switch (type) {
124 case '*':
125 case '?':
126 case '+':
127 ((ContentModel)content).getElements(elemVec);
128 break;
129 case ',':
130 case '|':
131 case '&':
132 for (ContentModel m=(ContentModel)content; m != null; m=m.next){
133 m.getElements(elemVec);
134 }
135 break;
136 default:
137 elemVec.addElement((Element)content);
138 }
139 }
140
141 private boolean valSet[];
142 private boolean val[];
143 // A cache used by first(). This cache was found to speed parsing
144 // by about 10% (based on measurements of the 4-12 code base after
145 // buffering was fixed).
146
147 /**
148 * Return true if the token could potentially be the
149 * first token in the input stream.
150 */
151 public boolean first(Object token) {
152 switch (type) {
153 case '*':
154 case '?':
155 case '+':
156 return ((ContentModel)content).first(token);
157
158 case ',':
159 for (ContentModel m = (ContentModel)content ; m != null ; m = m.next) {
160 if (m.first(token)) {
161 return true;
162 }
163 if (!m.empty()) {
164 return false;
165 }
166 }
167 return false;
168
169 case '|':
170 case '&': {
171 Element e = (Element) token;
172 if (valSet == null) {
173 valSet = new boolean[Element.maxIndex + 1];
174 val = new boolean[Element.maxIndex + 1];
175 // All Element instances are created before this ever executes
176 }
177 if (valSet[e.index]) {
178 return val[e.index];
179 }
180 for (ContentModel m = (ContentModel)content ; m != null ; m = m.next) {
181 if (m.first(token)) {
182 val[e.index] = true;
183 break;
184 }
185 }
186 valSet[e.index] = true;
187 return val[e.index];
188 }
189
190 default:
191 return (content == token);
192 // PENDING: refer to comment in ContentModelState
193 /*
194 if (content == token) {
195 return true;
196 }
197 Element e = (Element)content;
198 if (e.omitStart() && e.content != null) {
199 return e.content.first(token);
200 }
201 return false;
202 */
203 }
204 }
205
206 /**
207 * Return the element that must be next.
208 */
209 public Element first() {
210 switch (type) {
211 case '&':
212 case '|':
213 case '*':
214 case '?':
215 return null;
216
217 case '+':
218 case ',':
219 return ((ContentModel)content).first();
220
221 default:
222 return (Element)content;
223 }
224 }
225
226 /**
227 * Convert to a string.
228 */
229 public String toString() {
230 switch (type) {
231 case '*':
232 return content + "*";
233 case '?':
234 return content + "?";
235 case '+':
236 return content + "+";
237
238 case ',':
239 case '|':
240 case '&':
241 char data[] = {' ', (char)type, ' '};
242 String str = "";
243 for (ContentModel m = (ContentModel)content ; m != null ; m = m.next) {
244 str = str + m;
245 if (m.next != null) {
246 str += new String(data);
247 }
248 }
249 return "(" + str + ")";
250
251 default:
252 return content.toString();
253 }
254 }
255 }