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