1/*
2 * Copyright (c) 2003, 2013, Oracle and/or its affiliates. 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.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package javax.xml.bind.util;
27
28import org.xml.sax.ContentHandler;
29import org.xml.sax.DTDHandler;
30import org.xml.sax.EntityResolver;
31import org.xml.sax.ErrorHandler;
32import org.xml.sax.InputSource;
33import org.xml.sax.SAXException;
34import org.xml.sax.SAXNotRecognizedException;
35import org.xml.sax.SAXParseException;
36import org.xml.sax.XMLReader;
37import org.xml.sax.ext.LexicalHandler;
38import org.xml.sax.helpers.XMLFilterImpl;
39
40import javax.xml.bind.JAXBContext;
41import javax.xml.bind.JAXBException;
42import javax.xml.bind.Marshaller;
43import javax.xml.transform.sax.SAXSource;
44import org.xml.sax.XMLFilter;
45
46/**
47 * JAXP {@link javax.xml.transform.Source} implementation
48 * that marshals a JAXB-generated object.
49 *
50 * <p>
51 * This utility class is useful to combine JAXB with
52 * other Java/XML technologies.
53 *
54 * <p>
55 * The following example shows how to use JAXB to marshal a document
56 * for transformation by XSLT.
57 *
58 * <blockquote>
59 *    <pre>
60 *       MyObject o = // get JAXB content tree
61 *
62 *       // jaxbContext is a JAXBContext object from which 'o' is created.
63 *       JAXBSource source = new JAXBSource( jaxbContext, o );
64 *
65 *       // set up XSLT transformation
66 *       TransformerFactory tf = TransformerFactory.newInstance();
67 *       Transformer t = tf.newTransformer(new StreamSource("test.xsl"));
68 *
69 *       // run transformation
70 *       t.transform(source,new StreamResult(System.out));
71 *    </pre>
72 * </blockquote>
73 *
74 * <p>
75 * The fact that JAXBSource derives from SAXSource is an implementation
76 * detail. Thus in general applications are strongly discouraged from
77 * accessing methods defined on SAXSource. In particular,
78 * the setXMLReader and setInputSource methods shall never be called.
79 * The XMLReader object obtained by the getXMLReader method shall
80 * be used only for parsing the InputSource object returned by
81 * the getInputSource method.
82 *
83 * <p>
84 * Similarly the InputSource object obtained by the getInputSource
85 * method shall be used only for being parsed by the XMLReader object
86 * returned by the getXMLReader.
87 *
88 * @author
89 *      Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com)
90 * @since 1.6
91 */
92public class JAXBSource extends SAXSource {
93
94    /**
95     * Creates a new {@link javax.xml.transform.Source} for the given content object.
96     *
97     * @param   context
98     *      JAXBContext that was used to create
99     *      <code>contentObject</code>. This context is used
100     *      to create a new instance of marshaller and must not be null.
101     * @param   contentObject
102     *      An instance of a JAXB-generated class, which will be
103     *      used as a {@link javax.xml.transform.Source} (by marshalling it into XML).  It must
104     *      not be null.
105     * @throws JAXBException if an error is encountered while creating the
106     * JAXBSource or if either of the parameters are null.
107     */
108    public JAXBSource( JAXBContext context, Object contentObject )
109        throws JAXBException {
110
111        this(
112            ( context == null ) ?
113                assertionFailed( Messages.format( Messages.SOURCE_NULL_CONTEXT ) ) :
114                context.createMarshaller(),
115
116            ( contentObject == null ) ?
117                assertionFailed( Messages.format( Messages.SOURCE_NULL_CONTENT ) ) :
118                contentObject);
119    }
120
121    /**
122     * Creates a new {@link javax.xml.transform.Source} for the given content object.
123     *
124     * @param   marshaller
125     *      A marshaller instance that will be used to marshal
126     *      <code>contentObject</code> into XML. This must be
127     *      created from a JAXBContext that was used to build
128     *      <code>contentObject</code> and must not be null.
129     * @param   contentObject
130     *      An instance of a JAXB-generated class, which will be
131     *      used as a {@link javax.xml.transform.Source} (by marshalling it into XML).  It must
132     *      not be null.
133     * @throws JAXBException if an error is encountered while creating the
134     * JAXBSource or if either of the parameters are null.
135     */
136    public JAXBSource( Marshaller marshaller, Object contentObject )
137        throws JAXBException {
138
139        if( marshaller == null )
140            throw new JAXBException(
141                Messages.format( Messages.SOURCE_NULL_MARSHALLER ) );
142
143        if( contentObject == null )
144            throw new JAXBException(
145                Messages.format( Messages.SOURCE_NULL_CONTENT ) );
146
147        this.marshaller = marshaller;
148        this.contentObject = contentObject;
149
150        super.setXMLReader(pseudoParser);
151        // pass a dummy InputSource. We don't care
152        super.setInputSource(new InputSource());
153    }
154
155    private final Marshaller marshaller;
156    private final Object contentObject;
157
158    // this object will pretend as an XMLReader.
159    // no matter what parameter is specified to the parse method,
160    // it just parse the contentObject.
161    private final XMLReader pseudoParser = new XMLReader() {
162        public boolean getFeature(String name) throws SAXNotRecognizedException {
163            if(name.equals("http://xml.org/sax/features/namespaces"))
164                return true;
165            if(name.equals("http://xml.org/sax/features/namespace-prefixes"))
166                return false;
167            throw new SAXNotRecognizedException(name);
168        }
169
170        public void setFeature(String name, boolean value) throws SAXNotRecognizedException {
171            if(name.equals("http://xml.org/sax/features/namespaces") && value)
172                return;
173            if(name.equals("http://xml.org/sax/features/namespace-prefixes") && !value)
174                return;
175            throw new SAXNotRecognizedException(name);
176        }
177
178        public Object getProperty(String name) throws SAXNotRecognizedException {
179            if( "http://xml.org/sax/properties/lexical-handler".equals(name) ) {
180                return lexicalHandler;
181            }
182            throw new SAXNotRecognizedException(name);
183        }
184
185        public void setProperty(String name, Object value) throws SAXNotRecognizedException {
186            if( "http://xml.org/sax/properties/lexical-handler".equals(name) ) {
187                this.lexicalHandler = (LexicalHandler)value;
188                return;
189            }
190            throw new SAXNotRecognizedException(name);
191        }
192
193        private LexicalHandler lexicalHandler;
194
195        // we will store this value but never use it by ourselves.
196        private EntityResolver entityResolver;
197        public void setEntityResolver(EntityResolver resolver) {
198            this.entityResolver = resolver;
199        }
200        public EntityResolver getEntityResolver() {
201            return entityResolver;
202        }
203
204        private DTDHandler dtdHandler;
205        public void setDTDHandler(DTDHandler handler) {
206            this.dtdHandler = handler;
207        }
208        public DTDHandler getDTDHandler() {
209            return dtdHandler;
210        }
211
212        // SAX allows ContentHandler to be changed during the parsing,
213        // but JAXB doesn't. So this repeater will sit between those
214        // two components.
215        private XMLFilter repeater = new XMLFilterImpl();
216
217        public void setContentHandler(ContentHandler handler) {
218            repeater.setContentHandler(handler);
219        }
220        public ContentHandler getContentHandler() {
221            return repeater.getContentHandler();
222        }
223
224        private ErrorHandler errorHandler;
225        public void setErrorHandler(ErrorHandler handler) {
226            this.errorHandler = handler;
227        }
228        public ErrorHandler getErrorHandler() {
229            return errorHandler;
230        }
231
232        public void parse(InputSource input) throws SAXException {
233            parse();
234        }
235
236        public void parse(String systemId) throws SAXException {
237            parse();
238        }
239
240        public void parse() throws SAXException {
241            // parses a content object by using the given marshaller
242            // SAX events will be sent to the repeater, and the repeater
243            // will further forward it to an appropriate component.
244            try {
245                marshaller.marshal( contentObject, (XMLFilterImpl)repeater );
246            } catch( JAXBException e ) {
247                // wrap it to a SAXException
248                SAXParseException se =
249                    new SAXParseException( e.getMessage(),
250                        null, null, -1, -1, e );
251
252                // if the consumer sets an error handler, it is our responsibility
253                // to notify it.
254                if(errorHandler!=null)
255                    errorHandler.fatalError(se);
256
257                // this is a fatal error. Even if the error handler
258                // returns, we will abort anyway.
259                throw se;
260            }
261        }
262    };
263
264    /**
265     * Hook to throw exception from the middle of a contructor chained call
266     * to this
267     */
268    private static Marshaller assertionFailed( String message )
269        throws JAXBException {
270
271        throw new JAXBException( message );
272    }
273}
274