001 /*
002 * @(#)$Id: AbstractUnmarshallerImpl.java,v 1.7 2003/02/13 23:41:06 kk122374 Exp $
003 *
004 * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved.
005 *
006 * This software is the proprietary information of Sun Microsystems, Inc.
007 * Use is subject to license terms.
008 *
009 */
010 package javax.xml.bind.helpers;
011
012 import java.io.File;
013 import java.net.URL;
014 import java.net.MalformedURLException;
015 import javax.xml.parsers.SAXParserFactory;
016 import javax.xml.parsers.ParserConfigurationException;
017 import javax.xml.transform.Source;
018 import javax.xml.transform.sax.SAXSource;
019 import javax.xml.transform.stream.StreamSource;
020 import javax.xml.transform.dom.DOMSource;
021 import javax.xml.bind.JAXBException;
022 import javax.xml.bind.PropertyException;
023 import javax.xml.bind.UnmarshalException;
024 import javax.xml.bind.Unmarshaller;
025 import javax.xml.bind.ValidationEventHandler;
026 import org.xml.sax.XMLReader;
027 import org.xml.sax.InputSource;
028 import org.xml.sax.SAXException;
029
030 /**
031 * Partial default <tt>Unmarshaller</tt> implementation.
032 *
033 * <p>
034 * This class provides a partial default implementation for the
035 * {@link javax.xml.bind.Unmarshaller} interface.
036 *
037 * <p>
038 * A JAXB Provider has to implement three methods
039 * (getUnmarshallerHandler, unmarshal(Node), and
040 * unmarshal(XMLReader,InputSource)
041 *
042 * @author <ul><li>Kohsuke Kawaguchi, Sun Microsystems, Inc.</li></ul>
043 * @version $Revision: 1.7 $ $Date: 2003/02/13 23:41:06 $
044 * @see javax.xml.bind.Unmarshaller
045 * @since JAXB1.0
046 */
047 public abstract class AbstractUnmarshallerImpl implements Unmarshaller
048 {
049 /** handler that will be used to process errors and warnings during unmarshal */
050 private ValidationEventHandler eventHandler =
051 new DefaultValidationEventHandler();
052
053 /** whether or not the unmarshaller will validate */
054 private boolean validating = false;
055
056 /**
057 * XMLReader that will be used to parse a document.
058 */
059 private XMLReader reader = null;
060
061 /**
062 * Obtains a configured XMLReader.
063 *
064 * This method is used when the client-specified
065 * {@link SAXSource} object doesn't have XMLReader.
066 *
067 * {@link Unmarshaller} is not re-entrant, so we will
068 * only use one instance of XMLReader.
069 */
070 protected XMLReader getXMLReader() throws JAXBException {
071 if(reader==null) {
072 try {
073 SAXParserFactory parserFactory;
074 parserFactory = SAXParserFactory.newInstance();
075 parserFactory.setNamespaceAware(true);
076 // there is no point in asking a validation because
077 // there is no guarantee that the document will come with
078 // a proper schemaLocation.
079 parserFactory.setValidating(false);
080 reader = parserFactory.newSAXParser().getXMLReader();
081 } catch( ParserConfigurationException e ) {
082 throw new JAXBException(e);
083 } catch( SAXException e ) {
084 throw new JAXBException(e);
085 }
086 }
087 return reader;
088 }
089
090 public Object unmarshal( Source source ) throws JAXBException {
091 if( source == null ) {
092 throw new IllegalArgumentException(
093 Messages.format( Messages.MUST_NOT_BE_NULL, "source" ) );
094 }
095
096 if(source instanceof SAXSource)
097 return unmarshal( (SAXSource)source );
098 if(source instanceof StreamSource)
099 return unmarshal( streamSourceToInputSource((StreamSource)source));
100 if(source instanceof DOMSource)
101 return unmarshal( ((DOMSource)source).getNode() );
102
103 // we don't handle other types of Source
104 throw new IllegalArgumentException();
105 }
106
107 // use the client specified XMLReader contained in the SAXSource.
108 private final Object unmarshal( SAXSource source ) throws JAXBException {
109
110 XMLReader reader = source.getXMLReader();
111 if( reader == null )
112 reader = getXMLReader();
113
114 return unmarshal( reader, source.getInputSource() );
115 }
116
117 /**
118 * Unmarshals an object by using the specified XMLReader and the InputSource.
119 *
120 * The callee should call the setErrorHandler method of the XMLReader
121 * so that errors are passed to the client-specified ValidationEventHandler.
122 */
123 protected abstract Object unmarshal( XMLReader reader, InputSource source ) throws JAXBException;
124
125 public final Object unmarshal( InputSource source ) throws JAXBException {
126 if( source == null ) {
127 throw new IllegalArgumentException(
128 Messages.format( Messages.MUST_NOT_BE_NULL, "source" ) );
129 }
130
131 return unmarshal( getXMLReader(), source );
132 }
133
134
135 private Object unmarshal( String url ) throws JAXBException {
136 return unmarshal( new InputSource(url) );
137 }
138
139 public final Object unmarshal( URL url ) throws JAXBException {
140 if( url == null ) {
141 throw new IllegalArgumentException(
142 Messages.format( Messages.MUST_NOT_BE_NULL, "url" ) );
143 }
144
145 return unmarshal( url.toExternalForm() );
146 }
147
148 public final Object unmarshal( File f ) throws JAXBException {
149 if( f == null ) {
150 throw new IllegalArgumentException(
151 Messages.format( Messages.MUST_NOT_BE_NULL, "file" ) );
152 }
153
154 try {
155 // copied from JAXP
156 String path = f.getAbsolutePath();
157 if (File.separatorChar != '/')
158 path = path.replace(File.separatorChar, '/');
159 if (!path.startsWith("/"))
160 path = "/" + path;
161 if (!path.endsWith("/") && f.isDirectory())
162 path = path + "/";
163 return unmarshal(new URL("file", "", path));
164 } catch( MalformedURLException e ) {
165 throw new IllegalArgumentException(e.getMessage());
166 }
167 }
168
169 public final Object unmarshal( java.io.InputStream is )
170 throws JAXBException {
171
172 if( is == null ) {
173 throw new IllegalArgumentException(
174 Messages.format( Messages.MUST_NOT_BE_NULL, "is" ) );
175 }
176
177 InputSource isrc = new InputSource( is );
178 return unmarshal( isrc );
179 }
180
181 private static InputSource streamSourceToInputSource( StreamSource ss ) {
182 InputSource is = new InputSource();
183 is.setSystemId( ss.getSystemId() );
184 is.setByteStream( ss.getInputStream() );
185 is.setCharacterStream( ss.getReader() );
186
187 return is;
188 }
189
190
191 /**
192 * Indicates whether or not the Unmarshaller is configured to validate
193 * during unmarshal operations.
194 * <p>
195 * <i><b>Note:</b> I named this method isValidating() to stay in-line
196 * with JAXP, as opposed to naming it getValidating(). </i>
197 *
198 * @return true if the Unmarshaller is configured to validate during
199 * unmarshal operations, false otherwise
200 * @throws JAXBException if an error occurs while retrieving the validating
201 * flag
202 */
203 public boolean isValidating() throws JAXBException {
204 return validating;
205 }
206
207 /**
208 * Allow an application to register a validation event handler.
209 * <p>
210 * The validation event handler will be called by the JAXB Provider if any
211 * validation errors are encountered during calls to any of the
212 * <tt>unmarshal</tt> methods. If the client application does not register
213 * a validation event handler before invoking the unmarshal methods, then
214 * all validation events will be silently ignored and may result in
215 * unexpected behaviour.
216 *
217 * @param handler the validation event handler
218 * @throws JAXBException if an error was encountered while setting the
219 * event handler
220 */
221 public void setEventHandler(ValidationEventHandler handler)
222 throws JAXBException {
223
224 if( handler == null ) {
225 eventHandler = new DefaultValidationEventHandler();
226 } else {
227 eventHandler = handler;
228 }
229 }
230
231 /**
232 * Specifies whether or not the Unmarshaller should validate during
233 * unmarshal operations. By default, the <tt>Unmarshaller</tt> does
234 * not validate.
235 * <p>
236 * This method may only be invoked before or after calling one of the
237 * unmarshal methods.
238 *
239 * @param validating true if the Unmarshaller should validate during
240 * unmarshal, false otherwise
241 * @throws JAXBException if an error occurred while enabling or disabling
242 * validation at unmarshal time
243 */
244 public void setValidating(boolean validating) throws JAXBException {
245 this.validating = validating;
246 }
247
248 /**
249 * Return the current event handler or the default event handler if one
250 * hasn't been set.
251 *
252 * @return the current ValidationEventHandler or the default event handler
253 * if it hasn't been set
254 * @throws JAXBException if an error was encountered while getting the
255 * current event handler
256 */
257 public ValidationEventHandler getEventHandler() throws JAXBException {
258 return eventHandler;
259 }
260
261
262 /**
263 * Creates an UnmarshalException from a SAXException.
264 *
265 * This is an utility method provided for the derived classes.
266 *
267 * <p>
268 * When a provider-implemented ContentHandler wants to throw a
269 * JAXBException, it needs to wrap the exception by a SAXException.
270 * If the unmarshaller implementation blindly wrap SAXException
271 * by JAXBException, such an exception will be a JAXBException
272 * wrapped by a SAXException wrapped by another JAXBException.
273 * This is silly.
274 *
275 * <p>
276 * This method checks the nested exception of SAXException
277 * and reduce those excessive wrapping.
278 *
279 * @return the resulting UnmarshalException
280 */
281 protected UnmarshalException createUnmarshalException( SAXException e ) {
282 // check the nested exception to see if it's an UnmarshalException
283 Exception nested = e.getException();
284 if(nested instanceof UnmarshalException)
285 return (UnmarshalException)nested;
286
287 if(nested instanceof RuntimeException)
288 // typically this is an unexpected exception,
289 // just throw it rather than wrap it, so that the full stack
290 // trace can be displayed.
291 throw (RuntimeException)nested;
292
293
294 // otherwise simply wrap it
295 if(nested!=null)
296 return new UnmarshalException(nested);
297 else
298 return new UnmarshalException(e);
299 }
300
301 /**
302 * Default implementation of the setProperty method always
303 * throws PropertyException since there are no required
304 * properties. If a provider needs to handle additional
305 * properties, it should override this method in a derived class.
306 */
307 public void setProperty( String name, Object value )
308 throws PropertyException {
309
310 if( name == null ) {
311 throw new IllegalArgumentException(
312 Messages.format( Messages.MUST_NOT_BE_NULL, "name" ) );
313 }
314
315 throw new PropertyException(name, value);
316 }
317
318 /**
319 * Default implementation of the getProperty method always
320 * throws PropertyException since there are no required
321 * properties. If a provider needs to handle additional
322 * properties, it should override this method in a derived class.
323 */
324 public Object getProperty( String name )
325 throws PropertyException {
326
327 if( name == null ) {
328 throw new IllegalArgumentException(
329 Messages.format( Messages.MUST_NOT_BE_NULL, "name" ) );
330 }
331
332 throw new PropertyException(name);
333 }
334
335 }