1 /* GnomeXMLReader.java -
2 Copyright (C) 2004 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
.libxmlj
.sax
;
41 import java
.io
.FileNotFoundException
;
42 import java
.io
.InputStream
;
43 import java
.io
.IOException
;
44 import java
.net
.MalformedURLException
;
46 import java
.util
.Arrays
;
47 import java
.util
.ArrayList
;
48 import java
.util
.Iterator
;
49 import java
.util
.List
;
51 import org
.xml
.sax
.Attributes
;
52 import org
.xml
.sax
.ContentHandler
;
53 import org
.xml
.sax
.DTDHandler
;
54 import org
.xml
.sax
.EntityResolver
;
55 import org
.xml
.sax
.ErrorHandler
;
56 import org
.xml
.sax
.InputSource
;
57 import org
.xml
.sax
.Locator
;
58 import org
.xml
.sax
.SAXException
;
59 import org
.xml
.sax
.SAXNotRecognizedException
;
60 import org
.xml
.sax
.SAXNotSupportedException
;
61 import org
.xml
.sax
.SAXParseException
;
62 import org
.xml
.sax
.XMLReader
;
63 import org
.xml
.sax
.ext
.DeclHandler
;
64 import org
.xml
.sax
.ext
.LexicalHandler
;
66 import gnu
.xml
.libxmlj
.util
.NamedInputStream
;
67 import gnu
.xml
.libxmlj
.util
.StandaloneLocator
;
68 import gnu
.xml
.libxmlj
.util
.XMLJ
;
71 * A SAX2 parser that uses libxml2.
73 * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
75 public class GnomeXMLReader
84 private static final String FEATURES_PREFIX
=
85 "http://xml.org/sax/features/";
87 private static final List RECOGNIZED_FEATURES
=
88 Arrays
.asList (new String
[]
90 "external-general-entities",
91 "external-parameter-entities",
93 "lexical-handler/parameter-entities",
100 "use-entity-resolver2",
104 private static final String PROPERTIES_PREFIX
=
105 "http://xml.org/sax/properties/";
107 private static final List RECOGNIZED_PROPERTIES
=
108 Arrays
.asList (new String
[]
110 "declaration-handler",
118 private transient boolean standalone
;
119 private boolean namespaces
;
120 private boolean namespacePrefixes
;
121 private boolean validation
;
125 private ContentHandler contentHandler
;
127 private DTDHandler dtdHandler
;
129 private EntityResolver entityResolver
;
131 private ErrorHandler errorHandler
;
133 private DeclHandler declarationHandler
;
135 private LexicalHandler lexicalHandler
;
137 private GnomeLocator locator
;
139 // Namespace helper for handling callbacks
140 private transient Namespaces ns
;
142 // If true, do not invoke callback methods except endDocument
143 private transient boolean seenFatalError
;
145 private transient boolean seenStartDocument
;
147 private transient String base
;
149 public GnomeXMLReader ()
154 public GnomeXMLReader (boolean namespaces
, boolean validation
)
156 this.namespaces
= namespaces
;
157 this.validation
= validation
;
158 ns
= new Namespaces ();
161 public ContentHandler
getContentHandler ()
163 return contentHandler
;
166 public void setContentHandler (ContentHandler handler
)
168 contentHandler
= handler
;
171 public DTDHandler
getDTDHandler ()
176 public void setDTDHandler (DTDHandler handler
)
178 dtdHandler
= handler
;
181 public EntityResolver
getEntityResolver ()
183 return entityResolver
;
186 public void setEntityResolver (EntityResolver resolver
)
188 entityResolver
= resolver
;
191 public ErrorHandler
getErrorHandler ()
196 public void setErrorHandler (ErrorHandler handler
)
198 errorHandler
= handler
;
203 public boolean getFeature (String name
)
204 throws SAXNotRecognizedException
, SAXNotSupportedException
206 checkFeatureName (name
);
207 String key
= name
.substring (FEATURES_PREFIX
.length ());
208 if ("external-general-entities".equals (key
))
210 return validation
; // TODO check this
212 else if ("external-parameter-entities".equals (key
))
214 return validation
; // TODO check this
216 else if ("is-standalone".equals (key
))
220 else if ("namespaces".equals (key
))
224 else if ("namespace-prefixes".equals (key
))
226 return namespacePrefixes
;
228 else if ("resolve-dtd-uris".equals (key
))
232 else if ("validation".equals (key
))
242 public void setFeature (String name
, boolean value
)
243 throws SAXNotRecognizedException
, SAXNotSupportedException
245 checkFeatureName (name
);
246 String key
= name
.substring (FEATURES_PREFIX
.length ());
247 if ("namespaces".equals (key
))
251 else if ("namespace-prefixes".equals (key
))
253 namespacePrefixes
= value
;
255 else if ("validation".equals (key
))
262 * Check that the specified feature name is recognized.
264 static void checkFeatureName (String name
)
265 throws SAXNotRecognizedException
267 if (name
== null || !name
.startsWith (FEATURES_PREFIX
))
269 throw new SAXNotRecognizedException (name
);
271 String key
= name
.substring (FEATURES_PREFIX
.length ());
272 if (!RECOGNIZED_FEATURES
.contains (key
))
274 throw new SAXNotRecognizedException (name
);
280 public Object
getProperty (String name
)
281 throws SAXNotRecognizedException
, SAXNotSupportedException
283 checkPropertyName (name
);
284 String key
= name
.substring (PROPERTIES_PREFIX
.length ());
285 if ("declaration-handler".equals (key
))
287 return getDeclarationHandler ();
289 else if ("lexical-handler".equals (key
))
291 return getLexicalHandler ();
295 throw new SAXNotSupportedException (name
);
299 public void setProperty (String name
, Object value
)
300 throws SAXNotRecognizedException
, SAXNotSupportedException
302 checkPropertyName (name
);
303 String key
= name
.substring (PROPERTIES_PREFIX
.length ());
304 if ("declaration-handler".equals (key
))
306 setDeclarationHandler ((DeclHandler
) value
);
308 else if ("lexical-handler".equals (key
))
310 setLexicalHandler ((LexicalHandler
) value
);
314 public DeclHandler
getDeclarationHandler ()
316 return declarationHandler
;
319 public void setDeclarationHandler (DeclHandler declarationHandler
)
321 this.declarationHandler
= declarationHandler
;
324 public LexicalHandler
getLexicalHandler ()
326 return lexicalHandler
;
329 public void setLexicalHandler (LexicalHandler lexicalHandler
)
331 this.lexicalHandler
= lexicalHandler
;
335 * Check that the specified property name is recognized.
337 static void checkPropertyName (String name
)
338 throws SAXNotRecognizedException
340 if (!name
.startsWith (PROPERTIES_PREFIX
))
342 throw new SAXNotRecognizedException (name
);
344 String key
= name
.substring (PROPERTIES_PREFIX
.length ());
345 if (!RECOGNIZED_PROPERTIES
.contains (key
))
347 throw new SAXNotRecognizedException (name
);
353 public void parse (String systemId
)
354 throws IOException
, SAXException
359 url
= new URL (systemId
);
361 catch (MalformedURLException e
)
363 File file
= new File(systemId
);
366 throw new FileNotFoundException (systemId
);
368 String path
= file
.getAbsolutePath();
369 if (File
.separatorChar
!= '/')
371 path
= path
.replace (File
.separatorChar
, '/');
373 if (!path
.startsWith ("/"))
377 if (!path
.endsWith ("/") && file
.isDirectory ())
381 url
= new URL ("file:" + path
);
383 InputSource source
= new InputSource(url
.toString ());
384 source
.setByteStream (url
.openStream ());
388 public synchronized void parse (InputSource input
)
389 throws IOException
, SAXException
391 NamedInputStream in
= XMLJ
.getInputStream (input
);
392 byte[] detectBuffer
= in
.getDetectBuffer ();
393 String publicId
= input
.getPublicId ();
394 String systemId
= input
.getSystemId ();
395 base
= XMLJ
.getBaseURI (systemId
);
398 seenFatalError
= false;
399 seenStartDocument
= false;
400 if (systemId
!= null)
402 int lsi
= systemId
.lastIndexOf ('/');
405 base
= systemId
.substring (0, lsi
+ 1);
408 // Handle zero-length document
409 if (detectBuffer
== null)
411 startDocument (true);
412 fatalError ("No document element", 0, 0, publicId
, systemId
);
423 contentHandler
!= null,
425 entityResolver
!= null,
426 errorHandler
!= null,
427 declarationHandler
!= null,
428 lexicalHandler
!= null);
432 native void parseStream (InputStream in
,
438 boolean contentHandler
,
440 boolean entityResolver
,
441 boolean errorHandler
,
442 boolean declarationHandler
,
443 boolean lexicalHandler
)
444 throws IOException
, SAXException
;
446 String
getURI (String prefix
)
452 return ns
.getURI (prefix
);
455 // Callbacks from libxmlj
457 private void startDTD (String name
, String publicId
, String systemId
)
460 if (seenFatalError
|| lexicalHandler
== null)
466 systemId
= XMLJ
.getAbsoluteURI (base
, systemId
);
467 lexicalHandler
.startDTD (name
, publicId
, systemId
);
471 if (e
instanceof SAXException
)
473 throw (SAXException
) e
;
477 throw new SAXException (e
);
482 private void externalEntityDecl (String name
, String publicId
,
486 if (seenFatalError
|| declarationHandler
== null)
492 systemId
= XMLJ
.getAbsoluteURI (base
, systemId
);
493 declarationHandler
.externalEntityDecl (name
, publicId
, systemId
);
497 if (e
instanceof SAXException
)
499 throw (SAXException
) e
;
503 throw new SAXException (e
);
508 private void internalEntityDecl (String name
, String value
)
511 if (seenFatalError
|| declarationHandler
== null)
517 declarationHandler
.internalEntityDecl (name
, value
);
521 if (e
instanceof SAXException
)
523 throw (SAXException
) e
;
527 throw new SAXException (e
);
532 private InputStream
resolveEntity (String publicId
, String systemId
)
533 throws SAXException
, IOException
535 if (entityResolver
== null)
541 systemId
= XMLJ
.getAbsoluteURI (base
, systemId
);
542 InputSource source
= entityResolver
.resolveEntity (publicId
, systemId
);
543 return (source
== null) ?
null : XMLJ
.getInputStream (source
);
547 if (e
instanceof SAXException
)
549 throw (SAXException
) e
;
553 throw new SAXException (e
);
558 private void notationDecl (String name
, String publicId
, String systemId
)
561 if (seenFatalError
|| dtdHandler
== null)
567 systemId
= XMLJ
.getAbsoluteURI (base
, systemId
);
568 dtdHandler
.notationDecl (name
, publicId
, systemId
);
572 if (e
instanceof SAXException
)
574 throw (SAXException
) e
;
578 throw new SAXException (e
);
583 private void attributeDecl (String eName
, String aName
, String type
,
584 String mode
, String value
)
587 if (seenFatalError
|| declarationHandler
== null)
593 declarationHandler
.attributeDecl (eName
, aName
, type
, mode
, value
);
597 if (e
instanceof SAXException
)
599 throw (SAXException
) e
;
603 throw new SAXException (e
);
608 private void elementDecl (String name
, String model
)
611 if (seenFatalError
|| declarationHandler
== null)
617 declarationHandler
.elementDecl (name
, model
);
621 if (e
instanceof SAXException
)
623 throw (SAXException
) e
;
627 throw new SAXException (e
);
632 private void unparsedEntityDecl (String name
, String publicId
,
633 String systemId
, String notationName
)
636 if (seenFatalError
|| dtdHandler
== null)
642 systemId
= XMLJ
.getAbsoluteURI (base
, systemId
);
643 dtdHandler
.unparsedEntityDecl (name
, publicId
, systemId
,
648 if (e
instanceof SAXException
)
650 throw (SAXException
) e
;
654 throw new SAXException (e
);
659 private void setDocumentLocator (Object ctx
, Object loc
)
661 locator
= new GnomeLocator (ctx
, loc
);
662 if (seenFatalError
|| contentHandler
== null)
668 contentHandler
.setDocumentLocator (locator
);
675 private void startDocument (boolean standalone
)
678 this.standalone
= standalone
;
679 seenStartDocument
= true;
680 if (contentHandler
== null)
686 contentHandler
.startDocument ();
690 if (e
instanceof SAXException
)
692 throw (SAXException
) e
;
696 throw new SAXException (e
);
701 private void endDocument ()
704 if (contentHandler
== null)
710 contentHandler
.endDocument();
714 if (e
instanceof SAXException
)
716 throw (SAXException
) e
;
720 throw new SAXException (e
);
725 private void startElement(String name
, String
[] attrs
)
728 if (seenFatalError
|| contentHandler
== null)
734 XMLName xName
= new XMLName (this, name
);
737 // Handle defined namespaces
739 int len
= (attrs
== null) ?
0 : attrs
.length
;
742 ArrayList filtered
= new ArrayList (len
);
743 for (int i
= 0; i
< len
; i
+= 2)
745 String attName
= attrs
[i
];
746 String attValue
= attrs
[i
+ 1];
747 if (attName
.equals ("xmlns"))
749 startPrefixMapping ("", attValue
);
751 else if (attName
.startsWith ("xmlns:"))
753 startPrefixMapping (attName
.substring (6), attValue
);
757 filtered
.add (attName
);
758 filtered
.add (attValue
);
761 // Remove xmlns attributes
762 attrs
= new String
[filtered
.size ()];
763 filtered
.toArray (attrs
);
766 // Construct attributes
767 Attributes atts
= new StringArrayAttributes (this, attrs
);
768 contentHandler
.startElement (xName
.uri
, xName
.localName
, xName
.qName
,
773 if (e
instanceof SAXException
)
775 throw (SAXException
) e
;
779 throw new SAXException (e
);
784 private void endElement (String name
)
787 if (seenFatalError
|| contentHandler
== null)
793 XMLName xName
= new XMLName (this, name
);
794 String uri
= (xName
.uri
== null) ?
"" : xName
.uri
;
795 contentHandler
.endElement (uri
, xName
.localName
, xName
.qName
);
796 // Handle undefining namespaces
799 for (Iterator i
= ns
.currentPrefixes (); i
.hasNext (); )
801 endPrefixMapping ((String
) i
.next ());
803 ns
.pop (); // releases current depth
808 if (e
instanceof SAXException
)
810 throw (SAXException
) e
;
814 throw new SAXException (e
);
819 private void startPrefixMapping (String prefix
, String uri
)
822 if (seenFatalError
|| contentHandler
== null)
826 ns
.define (prefix
, uri
);
827 contentHandler
.startPrefixMapping (prefix
, uri
);
830 private void endPrefixMapping (String prefix
)
833 if (seenFatalError
|| contentHandler
== null)
837 contentHandler
.endPrefixMapping (prefix
);
840 private void characters (String text
)
843 if (seenFatalError
|| contentHandler
== null || text
== null)
849 char[] ch
= text
.toCharArray ();
850 contentHandler
.characters (ch
, 0, ch
.length
);
854 if (e
instanceof SAXException
)
856 throw (SAXException
) e
;
860 throw new SAXException (e
);
865 private void ignorableWhitespace (String text
)
868 if (seenFatalError
|| contentHandler
== null || text
== null)
874 char[] ch
= text
.toCharArray ();
875 contentHandler
.ignorableWhitespace (ch
, 0, ch
.length
);
879 if (e
instanceof SAXException
)
881 throw (SAXException
) e
;
885 throw new SAXException (e
);
890 private void processingInstruction (String target
, String data
)
893 if (seenFatalError
|| contentHandler
== null)
903 contentHandler
.processingInstruction (target
, data
);
907 if (e
instanceof SAXException
)
909 throw (SAXException
) e
;
913 throw new SAXException (e
);
918 private void comment (String text
)
921 if (seenFatalError
|| lexicalHandler
== null || text
== null)
927 char[] ch
= text
.toCharArray ();
928 lexicalHandler
.comment (ch
, 0, ch
.length
);
932 if (e
instanceof SAXException
)
934 throw (SAXException
) e
;
938 throw new SAXException (e
);
943 private void cdataBlock (String text
)
946 if (seenFatalError
|| text
== null)
952 if (lexicalHandler
== null)
958 lexicalHandler
.startCDATA();
960 lexicalHandler
.endCDATA();
965 if (e
instanceof SAXException
)
967 throw (SAXException
) e
;
971 throw new SAXException (e
);
976 private void warning (String message
,
977 int lineNumber
, int columnNumber
,
978 String publicId
, String systemId
)
981 if (seenFatalError
|| errorHandler
== null)
987 Locator l
= new StandaloneLocator (lineNumber
, columnNumber
,
989 errorHandler
.warning (new SAXParseException (message
, l
));
993 if (e
instanceof SAXException
)
995 throw (SAXException
) e
;
999 throw new SAXException (e
);
1004 private void error (String message
,
1005 int lineNumber
, int columnNumber
,
1006 String publicId
, String systemId
)
1009 if (seenFatalError
|| errorHandler
== null)
1015 Locator l
= new StandaloneLocator (lineNumber
, columnNumber
,
1016 publicId
, systemId
);
1017 errorHandler
.error (new SAXParseException (message
, l
));
1021 if (e
instanceof SAXException
)
1023 throw (SAXException
) e
;
1027 throw new SAXException (e
);
1032 private void fatalError (String message
,
1033 int lineNumber
, int columnNumber
,
1034 String publicId
, String systemId
)
1037 if (seenFatalError
|| errorHandler
== null)
1043 if (!seenStartDocument
)
1045 startDocument (false);
1047 seenFatalError
= true;
1048 Locator l
= new StandaloneLocator (lineNumber
, columnNumber
,
1049 publicId
, systemId
);
1050 errorHandler
.fatalError (new SAXParseException (message
, l
));
1054 if (e
instanceof SAXException
)
1056 throw (SAXException
) e
;
1060 throw new SAXException (e
);