author | jcbeyler |
Thu, 30 Aug 2018 09:47:12 -0700 | |
changeset 51594 | dc79850e0254 |
parent 47216 | 71c04702a3d5 |
permissions | -rw-r--r-- |
2 | 1 |
/* |
36110
91a3a6df36ec
8150203: Incremental update from build-infra project
ihse
parents:
34885
diff
changeset
|
2 |
* Copyright (c) 1998, 2016, Oracle and/or its affiliates. All rights reserved. |
2 | 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 |
|
5506 | 7 |
* published by the Free Software Foundation. Oracle designates this |
2 | 8 |
* particular file as subject to the "Classpath" exception as provided |
5506 | 9 |
* by Oracle in the LICENSE file that accompanied this code. |
2 | 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 |
* |
|
5506 | 21 |
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
22 |
* or visit www.oracle.com if you need additional information or have any |
|
23 |
* questions. |
|
2 | 24 |
*/ |
25 |
||
26 |
package build.tools.dtdbuilder; |
|
27 |
||
28 |
import javax.swing.text.html.parser.*; |
|
29 |
import java.net.URL; |
|
30 |
import java.io.IOException; |
|
31 |
import java.io.InputStream; |
|
32 |
import java.util.Enumeration; |
|
33 |
import java.util.Vector; |
|
34 |
import java.util.Hashtable; |
|
35 |
import java.util.BitSet; |
|
36 |
import java.text.MessageFormat; |
|
37 |
||
38 |
/** |
|
39 |
* A parser for DTDs. This parser roughly corresponds to the |
|
40 |
* rules specified in "The SGML Handbook" by Charles F. Goldfarb. |
|
41 |
* The end result of parsing the stream is a DTD object. |
|
42 |
* |
|
43 |
* |
|
44 |
* @see DTD |
|
45 |
* @see DTDInputStream |
|
46 |
* @author Arthur van Hoff |
|
47 |
*/ |
|
48 |
final |
|
49 |
class DTDParser implements DTDConstants { |
|
50 |
DTDBuilder dtd; |
|
51 |
DTDInputStream in; |
|
52 |
int ch; |
|
53 |
char str[] = new char[128]; |
|
54 |
int strpos = 0; |
|
55 |
int nerrors = 0; |
|
56 |
||
57 |
/** |
|
58 |
* Report an error. |
|
59 |
*/ |
|
60 |
void error(String err, String arg1, String arg2, String arg3) { |
|
61 |
nerrors++; |
|
62 |
||
63 |
String msgParams[] = {arg1, arg2, arg3}; |
|
64 |
||
65 |
String str = getSubstProp("dtderr." + err, msgParams); |
|
66 |
if (str == null) { |
|
67 |
str = err + "[" + arg1 + "," + arg2 + "," + arg3 + "]"; |
|
68 |
} |
|
69 |
System.err.println("line " + in.ln + ", dtd " + dtd + ": " + str); |
|
70 |
} |
|
71 |
void error(String err, String arg1, String arg2) { |
|
72 |
error(err, arg1, arg2, "?"); |
|
73 |
} |
|
74 |
void error(String err, String arg1) { |
|
75 |
error(err, arg1, "?", "?"); |
|
76 |
} |
|
77 |
void error(String err) { |
|
78 |
error(err, "?", "?", "?"); |
|
79 |
} |
|
80 |
||
81 |
private String getSubstProp(String propName, String args[]) { |
|
82 |
String prop = System.getProperty(propName); |
|
83 |
||
84 |
if (prop == null) { |
|
85 |
return null; |
|
86 |
} |
|
87 |
||
10110 | 88 |
return MessageFormat.format(prop, (Object[])args); |
2 | 89 |
} |
90 |
||
91 |
/** |
|
92 |
* Expect a character. |
|
93 |
*/ |
|
94 |
boolean expect(int c) throws IOException { |
|
95 |
if (ch != c) { |
|
96 |
char str[] = {(char)c}; |
|
97 |
error("expected", "'" + new String(str) + "'"); |
|
98 |
return false; |
|
99 |
} |
|
100 |
ch = in.read(); |
|
101 |
return true; |
|
102 |
} |
|
103 |
||
104 |
/** |
|
105 |
* Add a char to the string buffer. |
|
106 |
*/ |
|
107 |
void addString(int c) { |
|
108 |
if (strpos == str.length) { |
|
109 |
char newstr[] = new char[str.length * 2]; |
|
110 |
System.arraycopy(str, 0, newstr, 0, str.length); |
|
111 |
str = newstr; |
|
112 |
} |
|
113 |
str[strpos++] = (char)c; |
|
114 |
} |
|
115 |
||
116 |
/** |
|
117 |
* Get the string which was accumulated in the buffer. |
|
118 |
* Pos is the starting position of the string. |
|
119 |
*/ |
|
120 |
String getString(int pos) { |
|
121 |
char newstr[] = new char[strpos - pos]; |
|
122 |
System.arraycopy(str, pos, newstr, 0, strpos - pos); |
|
123 |
strpos = pos; |
|
124 |
return new String(newstr); |
|
125 |
} |
|
126 |
||
127 |
/** |
|
128 |
* Get the chars which were accumulated in the buffer. |
|
129 |
* Pos is the starting position of the string. |
|
130 |
*/ |
|
131 |
char[] getChars(int pos) { |
|
132 |
char newstr[] = new char[strpos - pos]; |
|
133 |
System.arraycopy(str, pos, newstr, 0, strpos - pos); |
|
134 |
strpos = pos; |
|
135 |
return newstr; |
|
136 |
} |
|
137 |
||
138 |
/** |
|
139 |
* Skip spaces. [5] 297:23 |
|
140 |
*/ |
|
141 |
void skipSpace() throws IOException { |
|
142 |
while (true) { |
|
143 |
switch (ch) { |
|
144 |
case '\n': |
|
145 |
case ' ': |
|
146 |
case '\t': |
|
147 |
ch = in.read(); |
|
148 |
break; |
|
149 |
||
150 |
default: |
|
151 |
return; |
|
152 |
} |
|
153 |
} |
|
154 |
} |
|
155 |
||
156 |
/** |
|
157 |
* Skip tag spaces (includes comments). [65] 372:1 |
|
158 |
*/ |
|
159 |
void skipParameterSpace() throws IOException { |
|
160 |
while (true) { |
|
161 |
switch (ch) { |
|
162 |
case '\n': |
|
163 |
case ' ': |
|
164 |
case '\t': |
|
165 |
ch = in.read(); |
|
166 |
break; |
|
167 |
case '-': |
|
168 |
if ((ch = in.read()) != '-') { |
|
169 |
in.push(ch); |
|
170 |
ch = '-'; |
|
171 |
return; |
|
172 |
} |
|
173 |
||
174 |
in.replace++; |
|
175 |
while (true) { |
|
176 |
switch (ch = in.read()) { |
|
177 |
case '-': |
|
178 |
if ((ch = in.read()) == '-') { |
|
179 |
ch = in.read(); |
|
180 |
in.replace--; |
|
181 |
skipParameterSpace(); |
|
182 |
return; |
|
183 |
} |
|
184 |
break; |
|
185 |
||
186 |
case -1: |
|
187 |
error("eof.arg", "comment"); |
|
188 |
in.replace--; |
|
189 |
return; |
|
190 |
} |
|
191 |
} |
|
192 |
default: |
|
193 |
return; |
|
194 |
} |
|
195 |
} |
|
196 |
} |
|
197 |
||
198 |
/** |
|
199 |
* Parse identifier. Uppercase characters are automatically |
|
200 |
* folded to lowercase. Returns falsed if no identifier is found. |
|
201 |
*/ |
|
10110 | 202 |
@SuppressWarnings("fallthrough") |
2 | 203 |
boolean parseIdentifier(boolean lower) throws IOException { |
204 |
switch (ch) { |
|
205 |
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': |
|
206 |
case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': |
|
207 |
case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': |
|
208 |
case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': |
|
209 |
case 'Y': case 'Z': |
|
210 |
if (lower) { |
|
211 |
ch = 'a' + (ch - 'A'); |
|
212 |
} |
|
10110 | 213 |
/* fall through */ |
2 | 214 |
|
215 |
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': |
|
216 |
case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': |
|
217 |
case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': |
|
218 |
case 's': case 't': case 'u': case 'v': case 'w': case 'x': |
|
219 |
case 'y': case 'z': |
|
220 |
break; |
|
221 |
||
222 |
default: |
|
223 |
return false; |
|
224 |
} |
|
225 |
||
226 |
addString(ch); |
|
227 |
ch = in.read(); |
|
228 |
parseNameToken(lower); |
|
229 |
return true; |
|
230 |
} |
|
231 |
||
232 |
/** |
|
233 |
* Parses name token. If <code>lower</code> is true, upper case letters |
|
234 |
* are folded to lower case. Returns falsed if no token is found. |
|
235 |
*/ |
|
10110 | 236 |
@SuppressWarnings("fallthrough") |
2 | 237 |
boolean parseNameToken(boolean lower) throws IOException { |
238 |
boolean first = true; |
|
239 |
||
240 |
while (true) { |
|
241 |
switch (ch) { |
|
242 |
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': |
|
243 |
case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': |
|
244 |
case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': |
|
245 |
case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': |
|
246 |
case 'Y': case 'Z': |
|
247 |
if (lower) { |
|
248 |
ch = 'a' + (ch - 'A'); |
|
249 |
} |
|
10110 | 250 |
/* fall through */ |
2 | 251 |
|
252 |
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': |
|
253 |
case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': |
|
254 |
case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': |
|
255 |
case 's': case 't': case 'u': case 'v': case 'w': case 'x': |
|
256 |
case 'y': case 'z': |
|
257 |
||
258 |
case '0': case '1': case '2': case '3': case '4': |
|
259 |
case '5': case '6': case '7': case '8': case '9': |
|
260 |
||
261 |
case '.': case '-': |
|
262 |
addString(ch); |
|
263 |
ch = in.read(); |
|
264 |
first = false; |
|
265 |
break; |
|
266 |
||
267 |
default: |
|
268 |
return !first; |
|
269 |
} |
|
270 |
} |
|
271 |
} |
|
272 |
||
273 |
/** |
|
274 |
* Parse a list of identifiers. |
|
275 |
*/ |
|
10110 | 276 |
Vector<String> parseIdentifierList(boolean lower) throws IOException { |
277 |
Vector<String> elems = new Vector<>(); |
|
2 | 278 |
skipSpace(); |
279 |
switch (ch) { |
|
280 |
case '(': |
|
281 |
ch = in.read(); |
|
282 |
skipParameterSpace(); |
|
283 |
while (parseNameToken(lower)) { |
|
284 |
elems.addElement(getString(0)); |
|
285 |
skipParameterSpace(); |
|
286 |
if (ch == '|') { |
|
287 |
ch = in.read(); |
|
288 |
skipParameterSpace(); |
|
289 |
} |
|
290 |
} |
|
291 |
expect(')'); |
|
292 |
skipParameterSpace(); |
|
293 |
break; |
|
294 |
||
295 |
default: |
|
296 |
if (!parseIdentifier(lower)) { |
|
297 |
error("expected", "identifier"); |
|
298 |
break; |
|
299 |
} |
|
300 |
elems.addElement(getString(0)); |
|
301 |
skipParameterSpace(); |
|
302 |
break; |
|
303 |
} |
|
304 |
return elems; |
|
305 |
} |
|
306 |
||
307 |
/** |
|
308 |
* Parse and Entity reference. Should be called when |
|
309 |
* a & is encountered. The data is put in the string buffer. |
|
310 |
* [59] 350:17 |
|
311 |
*/ |
|
312 |
private void parseEntityReference() throws IOException { |
|
313 |
int pos = strpos; |
|
314 |
||
315 |
if ((ch = in.read()) == '#') { |
|
316 |
int n = 0; |
|
317 |
ch = in.read(); |
|
318 |
if (((ch >= 'a') && (ch <= 'z')) || ((ch >= 'A') && (ch <= 'Z'))) { |
|
319 |
addString('#'); |
|
320 |
} else { |
|
321 |
while ((ch >= '0') && (ch <= '9')) { |
|
322 |
n = (n * 10) + ch - '0'; |
|
323 |
ch = in.read(); |
|
324 |
} |
|
325 |
if ((ch == ';') || (ch == '\n')) { |
|
326 |
ch = in.read(); |
|
327 |
} |
|
328 |
addString(n); |
|
329 |
return; |
|
330 |
} |
|
331 |
} |
|
332 |
||
333 |
while (true) { |
|
334 |
switch (ch) { |
|
335 |
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': |
|
336 |
case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': |
|
337 |
case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': |
|
338 |
case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': |
|
339 |
case 'Y': case 'Z': |
|
340 |
||
341 |
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': |
|
342 |
case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': |
|
343 |
case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': |
|
344 |
case 's': case 't': case 'u': case 'v': case 'w': case 'x': |
|
345 |
case 'y': case 'z': |
|
346 |
||
347 |
case '0': case '1': case '2': case '3': case '4': |
|
348 |
case '5': case '6': case '7': case '8': case '9': |
|
349 |
||
350 |
case '.': case '-': |
|
351 |
addString(ch); |
|
352 |
ch = in.read(); |
|
353 |
break; |
|
354 |
||
355 |
default: |
|
356 |
if (strpos == pos) { |
|
357 |
addString('&'); |
|
358 |
return; |
|
359 |
} |
|
360 |
String nm = getString(pos); |
|
361 |
Entity ent = dtd.getEntity(nm); |
|
362 |
if (ent == null) { |
|
363 |
error("undef.entref" + nm); |
|
364 |
return; |
|
365 |
} |
|
366 |
if ((ch == ';') || (ch == '\n')) { |
|
367 |
ch = in.read(); |
|
368 |
} |
|
369 |
char data[] = ent.getData(); |
|
370 |
for (int i = 0 ; i < data.length ; i++) { |
|
371 |
addString(data[i]); |
|
372 |
} |
|
373 |
return; |
|
374 |
} |
|
375 |
} |
|
376 |
} |
|
377 |
||
378 |
/** |
|
379 |
* Parse an entity declaration. |
|
380 |
* [101] 394:18 |
|
381 |
* REMIND: external entity type |
|
382 |
*/ |
|
383 |
private void parseEntityDeclaration() throws IOException { |
|
384 |
int type = GENERAL; |
|
385 |
||
386 |
skipSpace(); |
|
387 |
if (ch == '%') { |
|
388 |
ch = in.read(); |
|
389 |
type = PARAMETER; |
|
390 |
skipSpace(); |
|
391 |
} |
|
392 |
if (ch == '#') { |
|
393 |
addString('#'); |
|
394 |
ch = in.read(); |
|
395 |
} |
|
396 |
if (!parseIdentifier(false)) { |
|
397 |
error("expected", "identifier"); |
|
398 |
return; |
|
399 |
} |
|
400 |
String nm = getString(0); |
|
401 |
skipParameterSpace(); |
|
402 |
if (parseIdentifier(false)) { |
|
403 |
String tnm = getString(0); |
|
404 |
int t = Entity.name2type(tnm); |
|
405 |
if (t == 0) { |
|
406 |
error("invalid.arg", "entity type", tnm); |
|
407 |
} else { |
|
408 |
type |= t; |
|
409 |
} |
|
410 |
skipParameterSpace(); |
|
411 |
} |
|
412 |
||
413 |
if ((ch != '"') && (ch != '\'')) { |
|
414 |
error("expected", "entity value"); |
|
415 |
skipParameterSpace(); |
|
416 |
if (ch == '>') { |
|
417 |
ch = in.read(); |
|
418 |
} |
|
419 |
return; |
|
420 |
} |
|
421 |
||
422 |
int term = ch; |
|
423 |
ch = in.read(); |
|
424 |
while ((ch != -1) && (ch != term)) { |
|
425 |
if (ch == '&') { |
|
426 |
parseEntityReference(); |
|
427 |
} else { |
|
428 |
addString(ch & 0xFF); |
|
429 |
ch = in.read(); |
|
430 |
} |
|
431 |
} |
|
432 |
if (ch == term) { |
|
433 |
ch = in.read(); |
|
434 |
} |
|
435 |
if (in.replace == 0) { |
|
436 |
char data[] = getChars(0); |
|
437 |
dtd.defineEntity(nm, type, data); |
|
438 |
} else { |
|
439 |
strpos = 0; |
|
440 |
} |
|
441 |
skipParameterSpace(); |
|
442 |
expect('>'); |
|
443 |
} |
|
444 |
||
445 |
/** |
|
446 |
* Parse content model. |
|
447 |
* [126] 410:1 |
|
448 |
* REMIND: data tag group |
|
449 |
*/ |
|
450 |
ContentModel parseContentModel() throws IOException { |
|
451 |
ContentModel m = null; |
|
452 |
||
453 |
switch (ch) { |
|
454 |
case '(': |
|
455 |
ch = in.read(); |
|
456 |
skipParameterSpace(); |
|
457 |
ContentModel e = parseContentModel(); |
|
458 |
||
459 |
if (ch != ')') { |
|
460 |
m = new ContentModel(ch, e); |
|
461 |
do { |
|
462 |
ch = in.read(); |
|
463 |
skipParameterSpace(); |
|
464 |
e.next = parseContentModel(); |
|
465 |
if (e.next.type == m.type) { |
|
466 |
e.next = (ContentModel)e.next.content; |
|
467 |
} |
|
468 |
for (; e.next != null ; e = e.next); |
|
469 |
} while (ch == m.type); |
|
470 |
} else { |
|
471 |
m = new ContentModel(',', e); |
|
472 |
} |
|
473 |
expect(')'); |
|
474 |
break; |
|
475 |
||
476 |
case '#': |
|
477 |
ch = in.read(); |
|
478 |
if (parseIdentifier(true)) { |
|
479 |
m = new ContentModel('*', new ContentModel(dtd.getElement("#" + getString(0)))); |
|
480 |
} else { |
|
481 |
error("invalid", "content model"); |
|
482 |
} |
|
483 |
break; |
|
484 |
||
485 |
default: |
|
486 |
if (parseIdentifier(true)) { |
|
487 |
m = new ContentModel(dtd.getElement(getString(0))); |
|
488 |
} else { |
|
489 |
error("invalid", "content model"); |
|
490 |
} |
|
491 |
break; |
|
492 |
} |
|
493 |
||
494 |
switch (ch) { |
|
495 |
case '?': |
|
496 |
case '*': |
|
497 |
case '+': |
|
498 |
m = new ContentModel(ch, m); |
|
499 |
ch = in.read(); |
|
500 |
break; |
|
501 |
} |
|
502 |
skipParameterSpace(); |
|
503 |
||
504 |
return m; |
|
505 |
} |
|
506 |
||
507 |
/** |
|
508 |
* Parse element declaration. |
|
509 |
* [116] 405:6 |
|
510 |
*/ |
|
511 |
void parseElementDeclaration() throws IOException { |
|
10110 | 512 |
Vector<String> elems = parseIdentifierList(true); |
2 | 513 |
BitSet inclusions = null; |
514 |
BitSet exclusions = null; |
|
515 |
boolean omitStart = false; |
|
516 |
boolean omitEnd = false; |
|
517 |
||
518 |
if ((ch == '-') || (ch == 'O')) { |
|
519 |
omitStart = ch == 'O'; |
|
520 |
ch = in.read(); |
|
521 |
skipParameterSpace(); |
|
522 |
||
523 |
if ((ch == '-') || (ch == 'O')) { |
|
524 |
omitEnd = ch == 'O'; |
|
525 |
ch = in.read(); |
|
526 |
skipParameterSpace(); |
|
527 |
} else { |
|
528 |
expect('-'); |
|
529 |
} |
|
530 |
} |
|
531 |
||
532 |
int type = MODEL; |
|
533 |
ContentModel content = null; |
|
534 |
if (parseIdentifier(false)) { |
|
535 |
String nm = getString(0); |
|
536 |
type = Element.name2type(nm); |
|
537 |
if (type == 0) { |
|
538 |
error("invalid.arg", "content type", nm); |
|
539 |
type = EMPTY; |
|
540 |
} |
|
541 |
skipParameterSpace(); |
|
542 |
} else { |
|
543 |
content = parseContentModel(); |
|
544 |
} |
|
545 |
||
546 |
if ((type == MODEL) || (type == ANY)) { |
|
547 |
if (ch == '-') { |
|
548 |
ch = in.read(); |
|
10110 | 549 |
Vector<String> v = parseIdentifierList(true); |
2 | 550 |
exclusions = new BitSet(); |
10110 | 551 |
for (Enumeration<String> e = v.elements() ; e.hasMoreElements() ;) { |
552 |
exclusions.set(dtd.getElement(e.nextElement()).getIndex()); |
|
2 | 553 |
} |
554 |
} |
|
555 |
if (ch == '+') { |
|
556 |
ch = in.read(); |
|
10110 | 557 |
Vector<String> v = parseIdentifierList(true); |
2 | 558 |
inclusions = new BitSet(); |
10110 | 559 |
for (Enumeration<String> e = v.elements() ; e.hasMoreElements() ;) { |
560 |
inclusions.set(dtd.getElement(e.nextElement()).getIndex()); |
|
2 | 561 |
} |
562 |
} |
|
563 |
} |
|
564 |
expect('>'); |
|
565 |
||
566 |
if (in.replace == 0) { |
|
10110 | 567 |
for (Enumeration<String> e = elems.elements() ; e.hasMoreElements() ;) { |
568 |
dtd.defineElement(e.nextElement(), type, omitStart, omitEnd, content, exclusions, inclusions, null); |
|
2 | 569 |
} |
570 |
} |
|
571 |
} |
|
572 |
||
573 |
/** |
|
574 |
* Parse an attribute declared value. |
|
575 |
* [145] 422:6 |
|
576 |
*/ |
|
577 |
void parseAttributeDeclaredValue(AttributeList atts) throws IOException { |
|
578 |
if (ch == '(') { |
|
579 |
atts.values = parseIdentifierList(true); |
|
580 |
atts.type = NMTOKEN; |
|
581 |
return; |
|
582 |
} |
|
583 |
if (!parseIdentifier(false)) { |
|
584 |
error("invalid", "attribute value"); |
|
585 |
return; |
|
586 |
} |
|
10110 | 587 |
atts.type = AttributeList.name2type(getString(0)); |
2 | 588 |
skipParameterSpace(); |
589 |
if (atts.type == NOTATION) { |
|
590 |
atts.values = parseIdentifierList(true); |
|
591 |
} |
|
592 |
} |
|
593 |
||
594 |
/** |
|
595 |
* Parse an attribute value specification. |
|
596 |
* [33] 331:1 |
|
597 |
*/ |
|
10110 | 598 |
@SuppressWarnings("fallthrough") |
2 | 599 |
String parseAttributeValueSpecification() throws IOException { |
600 |
int delim = -1; |
|
601 |
switch (ch) { |
|
602 |
case '\'': |
|
603 |
case '"': |
|
604 |
delim = ch; |
|
605 |
ch = in.read(); |
|
606 |
} |
|
607 |
while (true) { |
|
608 |
switch (ch) { |
|
609 |
case -1: |
|
610 |
error("eof.arg", "attribute value"); |
|
611 |
return getString(0); |
|
612 |
||
613 |
case '&': |
|
614 |
parseEntityReference(); |
|
615 |
break; |
|
616 |
||
617 |
case ' ': |
|
618 |
case '\t': |
|
619 |
case '\n': |
|
620 |
if (delim == -1) { |
|
621 |
return getString(0); |
|
622 |
} |
|
623 |
addString(' '); |
|
624 |
ch = in.read(); |
|
625 |
break; |
|
626 |
||
627 |
case '\'': |
|
628 |
case '"': |
|
629 |
if (delim == ch) { |
|
630 |
ch = in.read(); |
|
631 |
return getString(0); |
|
632 |
} |
|
10110 | 633 |
/* fall through */ |
2 | 634 |
|
635 |
default: |
|
636 |
addString(ch & 0xFF); |
|
637 |
ch = in.read(); |
|
638 |
break; |
|
639 |
} |
|
640 |
} |
|
641 |
} |
|
642 |
||
643 |
/** |
|
644 |
* Parse an attribute default value. |
|
645 |
* [147] 425:1 |
|
646 |
*/ |
|
647 |
void parseAttributeDefaultValue(AttributeList atts) throws IOException { |
|
648 |
if (ch == '#') { |
|
649 |
ch = in.read(); |
|
650 |
if (!parseIdentifier(true)) { |
|
651 |
error("invalid", "attribute value"); |
|
652 |
return; |
|
653 |
} |
|
654 |
skipParameterSpace(); |
|
10110 | 655 |
atts.modifier = AttributeList.name2type(getString(0)); |
2 | 656 |
if (atts.modifier != FIXED) { |
657 |
return; |
|
658 |
} |
|
659 |
} |
|
660 |
atts.value = parseAttributeValueSpecification(); |
|
661 |
skipParameterSpace(); |
|
662 |
} |
|
663 |
||
664 |
/** |
|
665 |
* Parse an attribute definition list declaration. |
|
666 |
* [141] 420:15 |
|
667 |
* REMIND: associated notation name |
|
668 |
*/ |
|
669 |
void parseAttlistDeclaration() throws IOException { |
|
10110 | 670 |
Vector<String> elems = parseIdentifierList(true); |
2 | 671 |
AttributeList attlist = null, atts = null; |
672 |
||
673 |
while (parseIdentifier(true)) { |
|
674 |
if (atts == null) { |
|
675 |
attlist = atts = new AttributeList(getString(0)); |
|
676 |
} else { |
|
677 |
atts.next = new AttributeList(getString(0)); |
|
678 |
atts = atts.next; |
|
679 |
} |
|
680 |
skipParameterSpace(); |
|
681 |
parseAttributeDeclaredValue(atts); |
|
682 |
parseAttributeDefaultValue(atts); |
|
683 |
||
684 |
if ((atts.modifier == IMPLIED) && (atts.values != null) && (atts.values.size() == 1)) { |
|
685 |
atts.value = (String)atts.values.elementAt(0); |
|
686 |
} |
|
687 |
} |
|
688 |
||
689 |
expect('>'); |
|
690 |
||
691 |
if (in.replace == 0) { |
|
10110 | 692 |
for (Enumeration<String> e = elems.elements() ; e.hasMoreElements() ;) { |
693 |
dtd.defineAttributes(e.nextElement(), attlist); |
|
2 | 694 |
} |
695 |
} |
|
696 |
} |
|
697 |
||
698 |
/** |
|
699 |
* Parse an ignored section until ]]> is encountered. |
|
700 |
*/ |
|
701 |
void parseIgnoredSection() throws IOException { |
|
702 |
int depth = 1; |
|
703 |
in.replace++; |
|
704 |
while (true) { |
|
705 |
switch (ch) { |
|
706 |
case '<': |
|
707 |
if ((ch = in.read()) == '!') { |
|
708 |
if ((ch = in.read()) == '[') { |
|
709 |
ch = in.read(); |
|
710 |
depth++; |
|
711 |
} |
|
712 |
} |
|
713 |
break; |
|
714 |
case ']': |
|
715 |
if ((ch = in.read()) == ']') { |
|
716 |
if ((ch = in.read()) == '>') { |
|
717 |
ch = in.read(); |
|
718 |
if (--depth == 0) { |
|
719 |
in.replace--; |
|
720 |
return; |
|
721 |
} |
|
722 |
} |
|
723 |
} |
|
724 |
break; |
|
725 |
case -1: |
|
726 |
error("eof"); |
|
727 |
in.replace--; |
|
728 |
return; |
|
729 |
||
730 |
default: |
|
731 |
ch = in.read(); |
|
732 |
break; |
|
733 |
} |
|
734 |
} |
|
735 |
} |
|
736 |
||
737 |
/** |
|
738 |
* Parse a marked section declaration. |
|
739 |
* [93] 391:13 |
|
740 |
* REMIND: deal with all status keywords |
|
741 |
*/ |
|
742 |
void parseMarkedSectionDeclaration() throws IOException { |
|
743 |
ch = in.read(); |
|
744 |
skipSpace(); |
|
745 |
if (!parseIdentifier(true)) { |
|
746 |
error("expected", "section status keyword"); |
|
747 |
return; |
|
748 |
} |
|
749 |
String str = getString(0); |
|
750 |
skipSpace(); |
|
751 |
expect('['); |
|
752 |
if ("ignore".equals(str)) { |
|
753 |
parseIgnoredSection(); |
|
754 |
} else { |
|
755 |
if (!"include".equals(str)) { |
|
756 |
error("invalid.arg", "section status keyword", str); |
|
757 |
} |
|
758 |
parseSection(); |
|
759 |
expect(']'); |
|
760 |
expect(']'); |
|
761 |
expect('>'); |
|
762 |
} |
|
763 |
} |
|
764 |
||
765 |
/** |
|
766 |
* Parse an external identifier |
|
767 |
* [73] 379:1 |
|
768 |
*/ |
|
769 |
void parseExternalIdentifier() throws IOException { |
|
770 |
if (parseIdentifier(false)) { |
|
771 |
String id = getString(0); |
|
772 |
skipParameterSpace(); |
|
773 |
||
774 |
if (id.equals("PUBLIC")) { |
|
775 |
if ((ch == '\'') || (ch == '"')) { |
|
776 |
parseAttributeValueSpecification(); |
|
777 |
} else { |
|
778 |
error("expected", "public identifier"); |
|
779 |
} |
|
780 |
skipParameterSpace(); |
|
781 |
} else if (!id.equals("SYSTEM")) { |
|
782 |
error("invalid", "external identifier"); |
|
783 |
} |
|
784 |
if ((ch == '\'') || (ch == '"')) { |
|
785 |
parseAttributeValueSpecification(); |
|
786 |
} |
|
787 |
skipParameterSpace(); |
|
788 |
} |
|
789 |
} |
|
790 |
||
791 |
/** |
|
792 |
* Parse document type declaration. |
|
793 |
* [110] 403:1 |
|
794 |
*/ |
|
795 |
void parseDocumentTypeDeclaration() throws IOException { |
|
796 |
skipParameterSpace(); |
|
797 |
if (!parseIdentifier(true)) { |
|
798 |
error("expected", "identifier"); |
|
799 |
} else { |
|
800 |
skipParameterSpace(); |
|
801 |
} |
|
802 |
strpos = 0; |
|
803 |
parseExternalIdentifier(); |
|
804 |
||
805 |
if (ch == '[') { |
|
806 |
ch = in.read(); |
|
807 |
parseSection(); |
|
808 |
expect(']'); |
|
809 |
skipParameterSpace(); |
|
810 |
} |
|
811 |
expect('>'); |
|
812 |
} |
|
813 |
||
814 |
/** |
|
815 |
* Parse a section of the input upto EOF or ']'. |
|
816 |
*/ |
|
10110 | 817 |
@SuppressWarnings("fallthrough") |
2 | 818 |
void parseSection() throws IOException { |
819 |
while (true) { |
|
820 |
switch (ch) { |
|
821 |
case ']': |
|
822 |
return; |
|
823 |
||
824 |
case '<': |
|
825 |
switch (ch = in.read()) { |
|
826 |
case '!': |
|
827 |
switch (ch = in.read()) { |
|
828 |
case '[': |
|
829 |
parseMarkedSectionDeclaration(); |
|
830 |
break; |
|
831 |
||
832 |
case '-': |
|
833 |
skipParameterSpace(); |
|
834 |
expect('>'); |
|
835 |
break; |
|
836 |
||
837 |
default: |
|
838 |
if (parseIdentifier(true)) { |
|
839 |
String str = getString(0); |
|
840 |
||
841 |
if (str.equals("element")) { |
|
842 |
parseElementDeclaration(); |
|
843 |
||
844 |
} else if (str.equals("entity")) { |
|
845 |
parseEntityDeclaration(); |
|
846 |
||
847 |
} else if (str.equals("attlist")) { |
|
848 |
parseAttlistDeclaration(); |
|
849 |
||
850 |
} else if (str.equals("doctype")) { |
|
851 |
parseDocumentTypeDeclaration(); |
|
852 |
||
853 |
} else if (str.equals("usemap")) { |
|
854 |
error("ignoring", "usemap"); |
|
855 |
while ((ch != -1) && (ch != '>')) { |
|
856 |
ch = in.read(); |
|
857 |
} |
|
858 |
expect('>'); |
|
859 |
} else if (str.equals("shortref")) { |
|
860 |
error("ignoring", "shortref"); |
|
861 |
while ((ch != -1) && (ch != '>')) { |
|
862 |
ch = in.read(); |
|
863 |
} |
|
864 |
expect('>'); |
|
865 |
} else if (str.equals("notation")) { |
|
866 |
error("ignoring", "notation"); |
|
867 |
while ((ch != -1) && (ch != '>')) { |
|
868 |
ch = in.read(); |
|
869 |
} |
|
870 |
expect('>'); |
|
871 |
} else { |
|
872 |
error("markup"); |
|
873 |
} |
|
874 |
} else { |
|
875 |
error("markup"); |
|
876 |
while ((ch != -1) && (ch != '>')) { |
|
877 |
ch = in.read(); |
|
878 |
} |
|
879 |
expect('>'); |
|
880 |
} |
|
881 |
} |
|
882 |
} |
|
883 |
break; |
|
884 |
||
885 |
case -1: |
|
886 |
return; |
|
887 |
||
888 |
default: |
|
889 |
char str[] = {(char)ch}; |
|
890 |
error("invalid.arg", "character", "'" + new String(str) + "' / " + ch); |
|
10110 | 891 |
/* fall through */ |
2 | 892 |
|
893 |
case ' ': |
|
894 |
case '\t': |
|
895 |
case '\n': |
|
896 |
ch = in.read(); |
|
897 |
break; |
|
898 |
} |
|
899 |
} |
|
900 |
} |
|
901 |
||
902 |
/** |
|
903 |
* Parse a DTD. |
|
904 |
* @return the dtd or null if an error occurred. |
|
905 |
*/ |
|
906 |
DTD parse(InputStream in, DTDBuilder dtd) { |
|
907 |
try { |
|
908 |
this.dtd = dtd; |
|
909 |
this.in = new DTDInputStream(in, dtd); |
|
910 |
||
911 |
ch = this.in.read(); |
|
912 |
parseSection(); |
|
913 |
||
914 |
if (ch != -1) { |
|
915 |
error("premature"); |
|
916 |
} |
|
917 |
} catch (IOException e) { |
|
918 |
error("ioexception"); |
|
919 |
} catch (Exception e) { |
|
920 |
error("exception", e.getClass().getName(), e.getMessage()); |
|
921 |
e.printStackTrace(); |
|
922 |
} catch (ThreadDeath e) { |
|
923 |
error("terminated"); |
|
924 |
} |
|
925 |
return (nerrors > 0) ? null : dtd; |
|
926 |
} |
|
927 |
} |