1 /*
2 * Copyright 2006 Sun Microsystems, Inc. All Rights Reserved.
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
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
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 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26 package com.sun.xml.internal.xsom.parser;
27
28 import java.io.IOException;
29 import java.net.URL;
30
31 import javax.xml.parsers.ParserConfigurationException;
32 import javax.xml.parsers.SAXParserFactory;
33
34 import org.xml.sax.ContentHandler;
35 import org.xml.sax.EntityResolver;
36 import org.xml.sax.ErrorHandler;
37 import org.xml.sax.InputSource;
38 import org.xml.sax.Locator;
39 import org.xml.sax.SAXException;
40 import org.xml.sax.SAXParseException;
41 import org.xml.sax.XMLReader;
42 import org.xml.sax.helpers.XMLFilterImpl;
43
44 import com.sun.xml.internal.xsom.impl.parser.Messages;
45
46 /**
47 * Standard XMLParser implemented by using JAXP.
48 *
49 * @author
50 * Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com)
51 */
52 public class JAXPParser implements XMLParser {
53
54 private final SAXParserFactory factory;
55
56 public JAXPParser( SAXParserFactory factory ) {
57 factory.setNamespaceAware(true); // just in case
58 this.factory = factory;
59 }
60
61 public JAXPParser() {
62 this( SAXParserFactory.newInstance());
63 }
64
65
66
67
68
69
70
71 public void parse( InputSource source, ContentHandler handler,
72 ErrorHandler errorHandler, EntityResolver entityResolver )
73
74 throws SAXException, IOException {
75
76 try {
77 XMLReader reader = factory.newSAXParser().getXMLReader();
78 reader = new XMLReaderEx(reader);
79
80 reader.setContentHandler(handler);
81 if(errorHandler!=null)
82 reader.setErrorHandler(errorHandler);
83 if(entityResolver!=null)
84 reader.setEntityResolver(entityResolver);
85 reader.parse(source);
86 } catch( ParserConfigurationException e ) {
87 // in practice this won't happen
88 SAXParseException spe = new SAXParseException(e.getMessage(),null,e);
89 errorHandler.fatalError(spe);
90 throw spe;
91 }
92 }
93
94
95
96 /**
97 * XMLReader with improved error message for entity resolution failure.
98 *
99 * TODO: this class is completely stand-alone, so it shouldn't be
100 * an inner class.
101 */
102 private static class XMLReaderEx extends XMLFilterImpl {
103
104 private Locator locator;
105
106 XMLReaderEx( XMLReader parent ) {
107 this.setParent(parent);
108 }
109
110 /**
111 * Resolves entities and reports user-friendly error messages.
112 *
113 * <p>
114 * Some XML parser (at least Xerces) does not report much information
115 * when it fails to resolve an entity, which is often quite
116 * frustrating. For example, if you are behind a firewall and the
117 * schema contains a reference to www.w3.org, and there is no
118 * entity resolver, the parser will just throw an IOException
119 * that doesn't contain any information about where that reference
120 * occurs nor what it is accessing.
121 *
122 * <p>
123 * By implementing an EntityResolver and resolving the reference
124 * by ourselves, we can report an error message with all the
125 * necessary information to fix the problem.
126 *
127 * <p>
128 * Note that we still need to the client-specified entity resolver
129 * to let the application handle entity resolution. Here we just catch
130 * an IOException and add more information.
131 */
132 public InputSource resolveEntity(String publicId, String systemId) throws SAXException {
133 try {
134 InputSource is=null;
135
136 // ask the client-specified entity resolver first
137 if( this.getEntityResolver()!=null)
138 is = this.getEntityResolver().resolveEntity(publicId,systemId);
139 if( is!=null ) return is; // if that succeeds, fine.
140
141 // rather than returning null, resolve it now
142 // so that we can detect errors.
143 is = new InputSource( new URL(systemId).openStream() );
144 is.setSystemId(systemId);
145 is.setPublicId(publicId);
146 return is;
147 } catch( IOException e ) {
148 // catch this error and provide a nice error message, rather than
149 // just throwing this IOException.
150 SAXParseException spe = new SAXParseException(
151 Messages.format(Messages.ERR_ENTITY_RESOLUTION_FAILURE,
152 systemId, e.toString()), // use the toString method to get the class name
153 locator, e );
154 if(this.getErrorHandler()!=null)
155 this.getErrorHandler().fatalError(spe);
156 throw spe;
157 }
158 }
159
160 public void setDocumentLocator(Locator locator) {
161 super.setDocumentLocator(locator);
162 this.locator = locator;
163 }
164 }
165 }