2 Copyright (C) 2005 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
38 package gnu
.xml
.stream
;
40 import java
.io
.InputStream
;
41 import java
.io
.IOException
;
42 import java
.io
.Reader
;
44 import java
.util
.Iterator
;
46 import javax
.xml
.XMLConstants
;
47 import javax
.xml
.namespace
.QName
;
48 import javax
.xml
.stream
.Location
;
49 import javax
.xml
.stream
.XMLEventReader
;
50 import javax
.xml
.stream
.XMLReporter
;
51 import javax
.xml
.stream
.XMLResolver
;
52 import javax
.xml
.stream
.XMLStreamConstants
;
53 import javax
.xml
.stream
.XMLStreamException
;
54 import javax
.xml
.stream
.XMLStreamReader
;
56 import org
.xml
.sax
.ContentHandler
;
57 import org
.xml
.sax
.DTDHandler
;
58 import org
.xml
.sax
.EntityResolver
;
59 import org
.xml
.sax
.ErrorHandler
;
60 import org
.xml
.sax
.InputSource
;
61 import org
.xml
.sax
.Parser
;
62 import org
.xml
.sax
.SAXException
;
63 import org
.xml
.sax
.SAXNotRecognizedException
;
64 import org
.xml
.sax
.SAXNotSupportedException
;
65 import org
.xml
.sax
.SAXParseException
;
66 import org
.xml
.sax
.XMLReader
;
67 import org
.xml
.sax
.ext
.Attributes2
;
68 import org
.xml
.sax
.ext
.DeclHandler
;
69 import org
.xml
.sax
.ext
.EntityResolver2
;
70 import org
.xml
.sax
.ext
.LexicalHandler
;
71 import org
.xml
.sax
.ext
.Locator2
;
74 * JAXP SAX parser using an underlying StAX parser.
75 * This parser supports the following additional SAX features and
78 * <tr><th colspan='4'>Features</th></tr>
79 * <tr><td>http://gnu.org/sax/features/xml-base</td>
80 * <td colspan='2'>read/write</td>
81 * <td>Indicates or sets whether XML Base processing is enabled</td></tr>
82 * <tr><th colspan='4'>Properties</th></tr>
83 * <tr><td>http://gnu.org/sax/properties/base-uri</td>
84 * <td>read-only</td><td>String</td>
85 * <td>Returns the base URI of the current event</td></tr>
86 * <tr><td>http://gnu.org/sax/properties/document-xml-encoding</td>
87 * <td>read-only</td><td>String</td>
88 * <td>Returns the encoding specified in the XML declaration</td></tr>
91 * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
93 public class SAXParser
94 extends javax
.xml
.parsers
.SAXParser
95 implements XMLReader
, Attributes2
, Locator2
, XMLReporter
,
96 XMLParser
.XMLResolver2
99 ContentHandler contentHandler
;
100 DeclHandler declHandler
;
101 DTDHandler dtdHandler
;
102 EntityResolver entityResolver
;
103 ErrorHandler errorHandler
;
104 LexicalHandler lexicalHandler
;
106 boolean validating
= false;
107 boolean namespaceAware
= true;
108 boolean xIncludeAware
= false;
109 boolean stringInterning
= true;
110 boolean coalescing
= true;
111 boolean replaceERefs
= true;
112 boolean externalEntities
= true;
113 boolean supportDTD
= true;
114 boolean baseAware
= true;
117 XMLStreamReader reader
;
120 boolean xmlStandalone
;
128 SAXParser(boolean validating
, boolean namespaceAware
, boolean xIncludeAware
)
130 this.validating
= validating
;
131 this.namespaceAware
= namespaceAware
;
132 this.xIncludeAware
= xIncludeAware
;
137 public Parser
getParser()
143 public XMLReader
getXMLReader()
149 public boolean isNamespaceAware()
151 return namespaceAware
;
154 public boolean isValidating()
159 public void setProperty(String name
, Object value
)
160 throws SAXNotRecognizedException
, SAXNotSupportedException
163 throw new IllegalStateException("parsing in progress");
164 final String FEATURES
= "http://xml.org/sax/features/";
165 final String PROPERTIES
= "http://xml.org/sax/properties/";
166 final String GNU_FEATURES
= "http://gnu.org/sax/features/";
167 if ((FEATURES
+ "namespaces").equals(name
))
168 namespaceAware
= Boolean
.TRUE
.equals(value
);
169 else if ((FEATURES
+ "namespace-prefixes").equals(name
))
173 else if ((FEATURES
+ "string-interning").equals(name
))
174 stringInterning
= Boolean
.TRUE
.equals(value
);
175 else if ((FEATURES
+ "use-attributes2").equals(name
))
179 else if ((FEATURES
+ "validation").equals(name
))
180 validating
= Boolean
.TRUE
.equals(value
);
181 else if ((FEATURES
+ "external-general-entities").equals(name
))
182 externalEntities
= Boolean
.TRUE
.equals(value
);
183 else if ((FEATURES
+ "external-parameter-entities").equals(name
))
184 externalEntities
= Boolean
.TRUE
.equals(value
);
185 else if ((PROPERTIES
+ "declaration-handler").equals(name
))
186 declHandler
= (DeclHandler
) value
;
187 else if ((PROPERTIES
+ "lexical-handler").equals(name
))
188 lexicalHandler
= (LexicalHandler
) value
;
189 else if ((GNU_FEATURES
+ "xml-base").equals(name
))
190 baseAware
= Boolean
.TRUE
.equals(value
);
192 throw new SAXNotSupportedException(name
);
195 public Object
getProperty(String name
)
196 throws SAXNotRecognizedException
, SAXNotSupportedException
198 final String FEATURES
= "http://xml.org/sax/features/";
199 final String PROPERTIES
= "http://xml.org/sax/properties/";
200 final String GNU_FEATURES
= "http://gnu.org/sax/features/";
201 final String GNU_PROPERTIES
= "http://gnu.org/sax/properties/";
202 if ((GNU_FEATURES
+ "base-uri").equals(name
))
204 if ((FEATURES
+ "is-standalone").equals(name
))
205 return xmlStandalone ? Boolean
.TRUE
: Boolean
.FALSE
;
206 if ((FEATURES
+ "namespaces").equals(name
))
207 return namespaceAware ? Boolean
.TRUE
: Boolean
.FALSE
;
208 if ((FEATURES
+ "namespace-prefixes").equals(name
))
210 if ((FEATURES
+ "string-interning").equals(name
))
211 return stringInterning ? Boolean
.TRUE
: Boolean
.FALSE
;
212 if ((FEATURES
+ "use-attributes2").equals(name
))
214 if ((FEATURES
+ "use-locator2").equals(name
))
216 if ((FEATURES
+ "use-entity-resolver2").equals(name
))
217 return Boolean
.FALSE
;
218 if ((FEATURES
+ "validation").equals(name
))
219 return validating ? Boolean
.TRUE
: Boolean
.FALSE
;
220 if ((FEATURES
+ "external-general-entities").equals(name
))
221 return externalEntities ? Boolean
.TRUE
: Boolean
.FALSE
;
222 if ((FEATURES
+ "external-parameter-entities").equals(name
))
223 return externalEntities ? Boolean
.TRUE
: Boolean
.FALSE
;
224 if ((FEATURES
+ "xml-1.1").equals(name
))
226 if ((PROPERTIES
+ "declaration-handler").equals(name
))
228 if ((PROPERTIES
+ "document-xml-version").equals(name
))
230 if ((PROPERTIES
+ "lexical-handler").equals(name
))
231 return lexicalHandler
;
232 if ((GNU_FEATURES
+ "xml-base").equals(name
))
233 return baseAware ? Boolean
.TRUE
: Boolean
.FALSE
;
234 if ((GNU_PROPERTIES
+ "document-xml-encoding").equals(name
))
236 throw new SAXNotSupportedException(name
);
239 public boolean isXIncludeAware()
241 return xIncludeAware
;
249 xmlStandalone
= false;
254 public boolean getFeature(String name
)
255 throws SAXNotRecognizedException
, SAXNotSupportedException
257 Object ret
= getProperty(name
);
258 if (ret
instanceof Boolean
)
259 return ((Boolean
) ret
).booleanValue();
260 throw new SAXNotSupportedException(name
);
263 public void setFeature(String name
, boolean value
)
264 throws SAXNotRecognizedException
, SAXNotSupportedException
266 setProperty(name
, value ? Boolean
.TRUE
: Boolean
.FALSE
);
269 public void setEntityResolver(EntityResolver resolver
)
271 entityResolver
= resolver
;
274 public EntityResolver
getEntityResolver()
276 return entityResolver
;
279 public void setDTDHandler(DTDHandler handler
)
281 dtdHandler
= handler
;
284 public DTDHandler
getDTDHandler()
289 public void setContentHandler(ContentHandler handler
)
291 contentHandler
= handler
;
294 public ContentHandler
getContentHandler()
296 return contentHandler
;
299 public void setErrorHandler(ErrorHandler handler
)
301 errorHandler
= handler
;
304 public ErrorHandler
getErrorHandler()
309 public synchronized void parse(InputSource input
)
310 throws IOException
, SAXException
313 String systemId
= input
.getSystemId();
314 InputStream in
= input
.getByteStream();
315 boolean opened
= false;
317 parser
= new XMLParser(in
, systemId
,
330 Reader r
= input
.getCharacterStream();
332 parser
= new XMLParser(r
, systemId
,
346 if (systemId
== null)
347 throw new SAXException("No stream or system ID specified");
348 systemId
= XMLParser
.absolutize(null, systemId
);
349 in
= new URL(systemId
).openStream();
351 parser
= new XMLParser(in
, systemId
,
367 reader
= new XIncludeFilter(parser
, systemId
, namespaceAware
,
370 if (contentHandler
!= null)
371 contentHandler
.setDocumentLocator(this);
372 boolean startDocumentDone
= false;
375 while (parser
.hasNext())
377 int event
= parser
.next();
379 baseURI
= parser
.getXMLBase();
382 case XMLStreamConstants
.CHARACTERS
:
383 if (contentHandler
!= null)
385 char[] b
= reader
.getTextCharacters();
386 contentHandler
.characters(b
, 0, b
.length
);
389 case XMLStreamConstants
.SPACE
:
390 if (contentHandler
!= null)
392 char[] b
= reader
.getTextCharacters();
393 if (isIgnorableWhitespace(parser
, b
, false))
394 contentHandler
.ignorableWhitespace(b
, 0, b
.length
);
396 contentHandler
.characters(b
, 0, b
.length
);
399 case XMLStreamConstants
.CDATA
:
400 if (lexicalHandler
!= null)
401 lexicalHandler
.startCDATA();
402 if (contentHandler
!= null)
404 char[] b
= reader
.getTextCharacters();
405 if (isIgnorableWhitespace(parser
, b
, true))
406 contentHandler
.ignorableWhitespace(b
, 0, b
.length
);
408 contentHandler
.characters(b
, 0, b
.length
);
410 if (lexicalHandler
!= null)
411 lexicalHandler
.endCDATA();
413 case XMLStreamConstants
.START_ELEMENT
:
414 if (contentHandler
!= null)
416 QName name
= reader
.getName();
417 String uri
= name
.getNamespaceURI();
418 String localName
= name
.getLocalPart();
419 String prefix
= name
.getPrefix();
420 String qName
= localName
;
421 if (!"".equals(prefix
))
422 qName
= prefix
+ ":" + localName
;
430 int nc
= reader
.getNamespaceCount();
431 for (int i
= 0; i
< nc
; i
++)
433 String nsuri
= reader
.getNamespaceURI(i
);
434 String nsprefix
= reader
.getNamespacePrefix(i
);
435 if ("xml".equals(nsprefix
))
437 contentHandler
.startPrefixMapping(nsprefix
, nsuri
);
440 contentHandler
.startElement(uri
, localName
, qName
, this);
443 case XMLStreamConstants
.END_ELEMENT
:
444 if (contentHandler
!= null)
446 QName name
= reader
.getName();
447 String uri
= name
.getNamespaceURI();
448 String localName
= name
.getLocalPart();
449 String prefix
= name
.getPrefix();
450 String qName
= localName
;
451 if (!"".equals(prefix
))
452 qName
= prefix
+ ":" + localName
;
458 contentHandler
.endElement(uri
, localName
, qName
);
461 int nc
= reader
.getNamespaceCount();
462 for (int i
= 0; i
< nc
; i
++)
464 String nsprefix
= reader
.getNamespacePrefix(i
);
465 if ("xml".equals(nsprefix
))
467 contentHandler
.endPrefixMapping(nsprefix
);
472 case XMLStreamConstants
.COMMENT
:
473 if (lexicalHandler
!= null)
475 char[] b
= reader
.getTextCharacters();
476 lexicalHandler
.comment(b
, 0, b
.length
);
479 case XMLStreamConstants
.PROCESSING_INSTRUCTION
:
480 if (contentHandler
!= null)
482 String target
= reader
.getPITarget();
483 String data
= reader
.getPIData();
486 contentHandler
.processingInstruction(target
, data
);
489 case XMLStreamConstants
.START_ENTITY
:
490 if (lexicalHandler
!= null)
492 String name
= reader
.getText();
493 lexicalHandler
.startEntity(name
);
496 case XMLStreamConstants
.END_ENTITY
:
497 if (lexicalHandler
!= null)
499 String name
= reader
.getText();
500 lexicalHandler
.endEntity(name
);
503 case XMLStreamConstants
.START_DOCUMENT
:
504 encoding
= reader
.getEncoding();
505 xmlVersion
= reader
.getVersion();
506 xmlStandalone
= reader
.isStandalone();
507 xmlEncoding
= reader
.getCharacterEncodingScheme();
508 if (contentHandler
!= null)
509 contentHandler
.startDocument();
510 startDocumentDone
= true;
512 case XMLStreamConstants
.END_DOCUMENT
:
513 if (contentHandler
!= null)
514 contentHandler
.endDocument();
516 case XMLStreamConstants
.DTD
:
517 XMLParser
.Doctype doctype
= parser
.doctype
;
518 if (lexicalHandler
!= null)
520 String rootName
= doctype
.rootName
;
521 String publicId
= doctype
.publicId
;
522 String systemId2
= doctype
.systemId
;
523 lexicalHandler
.startDTD(rootName
, publicId
, systemId2
);
525 for (Iterator i
= doctype
.entryIterator(); i
.hasNext(); )
527 String entry
= (String
) i
.next();
528 char c
= entry
.charAt(0);
529 String name
= entry
.substring(1);
533 if (declHandler
!= null)
535 XMLParser
.ContentModel model
=
536 doctype
.getElementModel(name
);
537 declHandler
.elementDecl(name
, model
.text
);
543 if (declHandler
!= null)
545 for (Iterator j
= doctype
.attlistIterator(name
);
548 Map
.Entry att
= (Map
.Entry
) j
.next();
549 String aname
= (String
) att
.getKey();
550 XMLParser
.AttributeDecl decl
=
551 (XMLParser
.AttributeDecl
) att
.getValue();
552 String type
= decl
.type
;
553 String value
= decl
.value
;
555 switch (decl
.valueType
)
557 case XMLParser
.ATTRIBUTE_DEFAULT_FIXED
:
560 case XMLParser
.ATTRIBUTE_DEFAULT_REQUIRED
:
563 case XMLParser
.ATTRIBUTE_DEFAULT_IMPLIED
:
567 declHandler
.attributeDecl(name
, aname
,
575 Object entity
= doctype
.getEntity(name
);
576 if (entity
instanceof String
)
578 if (declHandler
!= null)
579 declHandler
.internalEntityDecl(name
,
584 XMLParser
.ExternalIds ids
=
585 (XMLParser
.ExternalIds
) entity
;
586 if (ids
.notationName
!= null)
588 if (dtdHandler
!= null)
590 String pub
= ids
.publicId
;
591 String url
= ids
.systemId
;
592 String not
= ids
.notationName
;
593 dtdHandler
.unparsedEntityDecl(name
,
601 if (declHandler
!= null)
603 String pub
= ids
.publicId
;
604 String url
= ids
.systemId
;
605 declHandler
.externalEntityDecl(name
,
615 if (dtdHandler
!= null)
617 XMLParser
.ExternalIds ids
=
618 doctype
.getNotation(name
);
619 String pub
= ids
.publicId
;
620 String url
= ids
.systemId
;
621 dtdHandler
.notationDecl(name
, pub
, url
);
627 if (lexicalHandler
!= null)
629 String comment
= doctype
.getComment(name
);
630 char[] b
= comment
.toCharArray();
631 lexicalHandler
.comment(b
, 0, b
.length
);
636 // Processing instruction
637 if (contentHandler
!= null)
639 String
[] pi
= doctype
.getPI(name
);
640 String target
= pi
[0];
644 contentHandler
.processingInstruction(target
, data
);
648 if (lexicalHandler
!= null)
649 lexicalHandler
.endDTD();
653 catch (XMLStreamException e
)
655 if (!startDocumentDone
&& contentHandler
!= null)
656 contentHandler
.startDocument();
657 SAXParseException e2
= new SAXParseException(e
.getMessage(), this);
659 if (errorHandler
!= null)
660 errorHandler
.fatalError(e2
);
661 if (contentHandler
!= null)
662 contentHandler
.endDocument();
674 * Indicates whether the specified characters are ignorable whitespace.
676 private boolean isIgnorableWhitespace(XMLParser reader
, char[] b
,
677 boolean testCharacters
)
678 throws XMLStreamException
680 XMLParser
.Doctype doctype
= reader
.doctype
;
683 String currentElement
= reader
.getCurrentElement();
684 // check for xml:space
685 int ac
= reader
.getAttributeCount();
686 for (int i
= 0; i
< ac
; i
++)
688 QName aname
= reader
.getAttributeQName(i
);
689 if ("space".equals(aname
.getLocalPart()) &&
690 XMLConstants
.XML_NS_URI
.equals(aname
.getNamespaceURI()))
692 if ("preserve".equals(reader
.getAttributeValue(i
)))
696 XMLParser
.ContentModel model
= doctype
.getElementModel(currentElement
);
697 if (model
== null || model
.type
!= XMLParser
.ContentModel
.ELEMENT
)
699 if (model
.external
&& xmlStandalone
)
701 boolean white
= true;
704 for (int i
= 0; i
< b
.length
; i
++)
706 if (b
[i
] != ' ' && b
[i
] != '\t' && b
[i
] != '\n' && b
[i
] != '\r')
716 public void parse(String systemId
)
717 throws IOException
, SAXException
719 parse(new InputSource(systemId
));
724 public int getIndex(String qName
)
726 int len
= reader
.getAttributeCount();
727 for (int i
= 0; i
< len
; i
++)
729 QName q
= reader
.getAttributeQName(i
);
730 String localName
= q
.getLocalPart();
731 String prefix
= q
.getPrefix();
732 String qn
= ("".equals(prefix
)) ? localName
: prefix
+ ":" + localName
;
733 if (qName
.equals(qn
))
739 public int getIndex(String uri
, String localName
)
741 int len
= reader
.getAttributeCount();
742 for (int i
= 0; i
< len
; i
++)
744 QName q
= reader
.getAttributeQName(i
);
745 String ln
= q
.getLocalPart();
746 String u
= q
.getNamespaceURI();
747 if (u
== null && uri
!= null)
749 if (u
!= null && !u
.equals(uri
))
751 if (ln
.equals(localName
))
757 public int getLength()
759 return reader
.getAttributeCount();
762 public String
getLocalName(int index
)
764 return reader
.getAttributeName(index
);
767 public String
getQName(int index
)
769 QName q
= reader
.getAttributeQName(index
);
770 String localName
= q
.getLocalPart();
771 String prefix
= q
.getPrefix();
772 return ("".equals(prefix
)) ? localName
: prefix
+ ":" + localName
;
775 public String
getType(int index
)
777 String ret
= reader
.getAttributeType(index
);
778 // SAX doesn't permit ENUMERATION?
779 return ("ENUMERATION".equals(ret
)) ?
"NMTOKEN" : ret
;
782 public String
getType(String qName
)
784 int index
= getIndex(qName
);
785 return (index
== -1) ?
null : getType(index
);
788 public String
getType(String uri
, String localName
)
790 int index
= getIndex(uri
, localName
);
791 return (index
== -1) ?
null : getType(index
);
794 public String
getURI(int index
)
796 String ret
= reader
.getAttributeNamespace(index
);
797 return (ret
== null) ?
"" : ret
;
800 public String
getValue(int index
)
802 return reader
.getAttributeValue(index
);
805 public String
getValue(String qName
)
807 int index
= getIndex(qName
);
808 return (index
== -1) ?
null : getValue(index
);
811 public String
getValue(String uri
, String localName
)
813 int index
= getIndex(uri
, localName
);
814 return (index
== -1) ?
null : getValue(index
);
817 public boolean isDeclared(int index
)
819 return parser
.isAttributeDeclared(index
);
822 public boolean isDeclared(String qName
)
824 int index
= getIndex(qName
);
825 return (index
== -1) ?
false : isDeclared(index
);
828 public boolean isDeclared(String uri
, String localName
)
830 int index
= getIndex(uri
, localName
);
831 return (index
== -1) ?
false : isDeclared(index
);
834 public boolean isSpecified(int index
)
836 return reader
.isAttributeSpecified(index
);
839 public boolean isSpecified(String qName
)
841 int index
= getIndex(qName
);
842 return (index
== -1) ?
false : isSpecified(index
);
845 public boolean isSpecified(String uri
, String localName
)
847 int index
= getIndex(uri
, localName
);
848 return (index
== -1) ?
false : isSpecified(index
);
853 public int getColumnNumber()
855 Location l
= reader
.getLocation();
856 return l
.getColumnNumber();
859 public int getLineNumber()
861 Location l
= reader
.getLocation();
862 return l
.getLineNumber();
865 public String
getPublicId()
870 public String
getSystemId()
872 Location l
= reader
.getLocation();
873 return l
.getLocationURI();
876 public String
getEncoding()
881 public String
getXMLVersion()
888 public InputStream
resolve(String uri
)
889 throws XMLStreamException
891 return resolve(null, uri
);
894 public InputStream
resolve(String publicId
, String systemId
)
895 throws XMLStreamException
897 if (entityResolver
!= null)
902 entityResolver
.resolveEntity(publicId
, systemId
);
904 return input
.getByteStream();
906 catch (SAXException e
)
908 XMLStreamException e2
= new XMLStreamException(e
.getMessage());
912 catch (IOException e
)
914 XMLStreamException e2
= new XMLStreamException(e
.getMessage());
922 public XMLEventReader
resolveAsXMLEventReader(String uri
)
923 throws XMLStreamException
929 public XMLStreamReader
resolveAsXMLStreamReader(String uri
)
930 throws XMLStreamException
938 public void report(String message
, String errorType
,
939 Object relatedInformation
, Location location
)
940 throws XMLStreamException
942 if (errorHandler
!= null)
946 errorHandler
.warning(new SAXParseException(message
, this));
948 catch (SAXException e
)
950 XMLStreamException e2
= new XMLStreamException(e
.getMessage());
957 public static void main(String
[] args
)
960 SAXParser parser
= new SAXParser();
961 InputSource input
= new InputSource(args
[0]);
962 parser
.parse(input
, new org
.xml
.sax
.helpers
.DefaultHandler());