1
/* StdXMLParser.java NanoXML/Java
4
* $Date: 2002/03/24 11:37:00 $
5
* $Name: RELEASE_2_2_1 $
7
* This file is part of NanoXML 2 for Java.
8
* Copyright (C) 2000-2002 Marc De Scheemaecker, All Rights Reserved.
10
* This software is provided 'as-is', without any express or implied warranty.
11
* In no event will the authors be held liable for any damages arising from the
12
* use of this software.
14
* Permission is granted to anyone to use this software for any purpose,
15
* including commercial applications, and to alter it and redistribute it
16
* freely, subject to the following restrictions:
18
* 1. The origin of this software must not be misrepresented; you must not
19
* claim that you wrote the original software. If you use this software in
20
* a product, an acknowledgment in the product documentation would be
21
* appreciated but is not required.
23
* 2. Altered source versions must be plainly marked as such, and must not be
24
* misrepresented as being the original software.
26
* 3. This notice may not be removed or altered from any source distribution.
29
package processing.xml;
32
import java.io.Reader;
33
import java.util.Enumeration;
34
import java.util.Properties;
35
import java.util.Vector;
39
* StdXMLParser is the core parser of NanoXML.
41
* @author Marc De Scheemaecker
42
* @version $Name: RELEASE_2_2_1 $, $Revision: 1.5 $
44
public class StdXMLParser {
47
* The builder which creates the logical structure of the XML data.
49
private StdXMLBuilder builder;
53
* The reader from which the parser retrieves its data.
55
private StdXMLReader reader;
59
* The entity resolver.
61
private XMLEntityResolver entityResolver;
65
* The validator that will process entity references and validate the XML
68
private XMLValidator validator;
72
* Creates a new parser.
77
this.validator = null;
79
this.entityResolver = new XMLEntityResolver();
84
* Cleans up the object when it's destroyed.
86
protected void finalize()
91
this.entityResolver = null;
92
this.validator = null;
98
* Sets the builder which creates the logical structure of the XML data.
100
* @param builder the non-null builder
102
public void setBuilder(StdXMLBuilder builder)
104
this.builder = builder;
109
* Returns the builder which creates the logical structure of the XML data.
111
* @return the builder
113
public StdXMLBuilder getBuilder()
120
* Sets the validator that validates the XML data.
122
* @param validator the non-null validator
124
public void setValidator(XMLValidator validator)
126
this.validator = validator;
131
* Returns the validator that validates the XML data.
133
* @return the validator
135
public XMLValidator getValidator()
137
return this.validator;
142
* Sets the entity resolver.
144
* @param resolver the non-null resolver
146
public void setResolver(XMLEntityResolver resolver)
148
this.entityResolver = resolver;
153
* Returns the entity resolver.
155
* @return the non-null resolver
157
public XMLEntityResolver getResolver()
159
return this.entityResolver;
164
* Sets the reader from which the parser retrieves its data.
166
* @param reader the reader
168
public void setReader(StdXMLReader reader)
170
this.reader = reader;
175
* Returns the reader from which the parser retrieves its data.
179
public StdXMLReader getReader()
186
* Parses the data and lets the builder create the logical data structure.
188
* @return the logical structure built by the builder
190
* @throws net.n3.nanoxml.XMLException
191
* if an error occurred reading or parsing the data
193
public Object parse()
197
this.builder.startBuilding(this.reader.getSystemID(),
198
this.reader.getLineNr());
200
return this.builder.getResult();
201
} catch (XMLException e) {
203
} catch (Exception e) {
204
throw new XMLException(e);
210
* Scans the XML data for elements.
212
* @throws java.lang.Exception
213
* if something went wrong
215
protected void scanData() throws Exception {
216
while ((! this.reader.atEOF()) && (this.builder.getResult() == null)) {
217
String str = XMLUtil.read(this.reader, '&');
218
char ch = str.charAt(0);
220
XMLUtil.processEntity(str, this.reader, this.entityResolver);
226
this.scanSomeTag(false, // don't allow CDATA
227
null, // no default namespace
239
XMLUtil.errorInvalidInput(reader.getSystemID(),
242
+ Integer.toHexString((int) ch)
252
* @param allowCDATA true if CDATA sections are allowed at this point
253
* @param defaultNamespace the default namespace URI (or null)
254
* @param namespaces list of defined namespaces
256
* @throws java.lang.Exception
257
* if something went wrong
259
protected void scanSomeTag(boolean allowCDATA,
260
String defaultNamespace,
261
Properties namespaces)
264
String str = XMLUtil.read(this.reader, '&');
265
char ch = str.charAt(0);
268
XMLUtil.errorUnexpectedEntity(reader.getSystemID(),
279
this.processSpecialTag(allowCDATA);
283
this.reader.unread(ch);
284
this.processElement(defaultNamespace, namespaces);
290
* Processes a "processing instruction".
292
* @throws java.lang.Exception
293
* if something went wrong
295
protected void processPI()
298
XMLUtil.skipWhitespace(this.reader, null);
299
String target = XMLUtil.scanIdentifier(this.reader);
300
XMLUtil.skipWhitespace(this.reader, null);
301
Reader r = new PIReader(this.reader);
303
if (!target.equalsIgnoreCase("xml")) {
304
this.builder.newProcessingInstruction(target, r);
312
* Processes a tag that starts with a bang (<!...>).
314
* @param allowCDATA true if CDATA sections are allowed at this point
316
* @throws java.lang.Exception
317
* if something went wrong
319
protected void processSpecialTag(boolean allowCDATA)
322
String str = XMLUtil.read(this.reader, '&');
323
char ch = str.charAt(0);
326
XMLUtil.errorUnexpectedEntity(reader.getSystemID(),
336
XMLUtil.errorUnexpectedCDATA(reader.getSystemID(),
343
this.processDocType();
347
XMLUtil.skipComment(this.reader);
354
* Processes a CDATA section.
356
* @throws java.lang.Exception
357
* if something went wrong
359
protected void processCDATA() throws Exception {
360
if (! XMLUtil.checkLiteral(this.reader, "CDATA[")) {
361
XMLUtil.errorExpectedInput(reader.getSystemID(),
366
this.validator.PCDataAdded(this.reader.getSystemID(),
367
this.reader.getLineNr());
368
Reader r = new CDATAReader(this.reader);
369
this.builder.addPCData(r, this.reader.getSystemID(),
370
this.reader.getLineNr());
376
* Processes a document type declaration.
378
* @throws java.lang.Exception
379
* if an error occurred reading or parsing the data
381
protected void processDocType() throws Exception {
382
if (! XMLUtil.checkLiteral(this.reader, "OCTYPE")) {
383
XMLUtil.errorExpectedInput(reader.getSystemID(),
389
XMLUtil.skipWhitespace(this.reader, null);
390
String systemID = null;
391
StringBuffer publicID = new StringBuffer();
392
/*String rootElement =*/ XMLUtil.scanIdentifier(this.reader);
393
//System.out.println("rootElement is " + rootElement);
394
XMLUtil.skipWhitespace(this.reader, null);
395
char ch = this.reader.read();
398
systemID = XMLUtil.scanPublicID(publicID, reader);
399
XMLUtil.skipWhitespace(this.reader, null);
400
ch = this.reader.read();
401
} else if (ch == 'S') {
402
systemID = XMLUtil.scanSystemID(reader);
403
XMLUtil.skipWhitespace(this.reader, null);
404
ch = this.reader.read();
408
this.validator.parseDTD(publicID.toString(),
412
XMLUtil.skipWhitespace(this.reader, null);
413
ch = this.reader.read();
417
XMLUtil.errorExpectedInput(reader.getSystemID(),
422
// TODO DTD checking is currently disabled, because it breaks
423
// applications that don't have access to a net connection
424
// (since it insists on going and checking out the DTD).
426
if (systemID != null) {
427
Reader r = this.reader.openStream(publicID.toString(), systemID);
428
this.reader.startNewStream(r);
429
this.reader.setSystemID(systemID);
430
this.reader.setPublicID(publicID.toString());
431
this.validator.parseDTD(publicID.toString(),
441
* Processes a regular element.
443
* @param defaultNamespace the default namespace URI (or null)
444
* @param namespaces list of defined namespaces
446
* @throws java.lang.Exception
447
* if something went wrong
449
protected void processElement(String defaultNamespace,
450
Properties namespaces)
453
String fullName = XMLUtil.scanIdentifier(this.reader);
454
String name = fullName;
455
XMLUtil.skipWhitespace(this.reader, null);
456
String prefix = null;
457
int colonIndex = name.indexOf(':');
459
if (colonIndex > 0) {
460
prefix = name.substring(0, colonIndex);
461
name = name.substring(colonIndex + 1);
464
Vector<String> attrNames = new Vector<String>();
465
Vector<String> attrValues = new Vector<String>();
466
Vector<String> attrTypes = new Vector<String>();
468
this.validator.elementStarted(fullName,
469
this.reader.getSystemID(),
470
this.reader.getLineNr());
474
ch = this.reader.read();
476
if ((ch == '/') || (ch == '>')) {
480
this.reader.unread(ch);
481
this.processAttribute(attrNames, attrValues, attrTypes);
482
XMLUtil.skipWhitespace(this.reader, null);
485
Properties extraAttributes = new Properties();
486
this.validator.elementAttributesProcessed(fullName,
488
this.reader.getSystemID(),
489
this.reader.getLineNr());
490
Enumeration<?> en = extraAttributes.keys();
492
while (en.hasMoreElements()) {
493
String key = (String) en.nextElement();
494
String value = extraAttributes.getProperty(key);
495
attrNames.addElement(key);
496
attrValues.addElement(value);
497
attrTypes.addElement("CDATA");
500
for (int i = 0; i < attrNames.size(); i++) {
501
String key = (String) attrNames.elementAt(i);
502
String value = (String) attrValues.elementAt(i);
503
//String type = (String) attrTypes.elementAt(i);
505
if (key.equals("xmlns")) {
506
defaultNamespace = value;
507
} else if (key.startsWith("xmlns:")) {
508
namespaces.put(key.substring(6), value);
512
if (prefix == null) {
513
this.builder.startElement(name, prefix, defaultNamespace,
514
this.reader.getSystemID(),
515
this.reader.getLineNr());
517
this.builder.startElement(name, prefix,
518
namespaces.getProperty(prefix),
519
this.reader.getSystemID(),
520
this.reader.getLineNr());
523
for (int i = 0; i < attrNames.size(); i++) {
524
String key = (String) attrNames.elementAt(i);
526
if (key.startsWith("xmlns")) {
530
String value = (String) attrValues.elementAt(i);
531
String type = (String) attrTypes.elementAt(i);
532
colonIndex = key.indexOf(':');
534
if (colonIndex > 0) {
535
String attPrefix = key.substring(0, colonIndex);
536
key = key.substring(colonIndex + 1);
537
this.builder.addAttribute(key, attPrefix,
538
namespaces.getProperty(attPrefix),
541
this.builder.addAttribute(key, null, null, value, type);
545
if (prefix == null) {
546
this.builder.elementAttributesProcessed(name, prefix,
549
this.builder.elementAttributesProcessed(name, prefix,
551
.getProperty(prefix));
555
if (this.reader.read() != '>') {
556
XMLUtil.errorExpectedInput(reader.getSystemID(),
561
this.validator.elementEnded(name,
562
this.reader.getSystemID(),
563
this.reader.getLineNr());
565
if (prefix == null) {
566
this.builder.endElement(name, prefix, defaultNamespace);
568
this.builder.endElement(name, prefix,
569
namespaces.getProperty(prefix));
575
StringBuffer buffer = new StringBuffer(16);
582
XMLUtil.skipWhitespace(this.reader, buffer);
583
str = XMLUtil.read(this.reader, '&');
585
if ((str.charAt(0) == '&') && (str.charAt(1) != '#')) {
586
XMLUtil.processEntity(str, this.reader,
587
this.entityResolver);
593
if (str.charAt(0) == '<') {
594
str = XMLUtil.read(this.reader, '\0');
596
if (str.charAt(0) == '/') {
597
XMLUtil.skipWhitespace(this.reader, null);
598
str = XMLUtil.scanIdentifier(this.reader);
600
if (! str.equals(fullName)) {
601
XMLUtil.errorWrongClosingTag(reader.getSystemID(),
606
XMLUtil.skipWhitespace(this.reader, null);
608
if (this.reader.read() != '>') {
609
XMLUtil.errorClosingTagNotEmpty(reader.getSystemID(),
613
this.validator.elementEnded(fullName,
614
this.reader.getSystemID(),
615
this.reader.getLineNr());
616
if (prefix == null) {
617
this.builder.endElement(name, prefix, defaultNamespace);
619
this.builder.endElement(name, prefix,
620
namespaces.getProperty(prefix));
624
this.reader.unread(str.charAt(0));
625
this.scanSomeTag(true, //CDATA allowed
627
(Properties) namespaces.clone());
630
if (str.charAt(0) == '&') {
631
ch = XMLUtil.processCharLiteral(str);
634
reader.unread(str.charAt(0));
636
this.validator.PCDataAdded(this.reader.getSystemID(),
637
this.reader.getLineNr());
638
Reader r = new ContentReader(this.reader,
641
this.builder.addPCData(r, this.reader.getSystemID(),
642
this.reader.getLineNr());
650
* Processes an attribute of an element.
652
* @param attrNames contains the names of the attributes.
653
* @param attrValues contains the values of the attributes.
654
* @param attrTypes contains the types of the attributes.
656
* @throws java.lang.Exception
657
* if something went wrong
659
protected void processAttribute(Vector<String> attrNames,
660
Vector<String> attrValues,
661
Vector<String> attrTypes)
664
String key = XMLUtil.scanIdentifier(this.reader);
665
XMLUtil.skipWhitespace(this.reader, null);
667
if (! XMLUtil.read(this.reader, '&').equals("=")) {
668
XMLUtil.errorExpectedInput(reader.getSystemID(),
673
XMLUtil.skipWhitespace(this.reader, null);
674
String value = XMLUtil.scanString(this.reader, '&',
675
this.entityResolver);
676
attrNames.addElement(key);
677
attrValues.addElement(value);
678
attrTypes.addElement("CDATA");
679
this.validator.attributeAdded(key, value,
680
this.reader.getSystemID(),
681
this.reader.getLineNr());