001    /*
002     * @(#)$Id: JAXBSource.java,v 1.9 2003/03/05 23:22:53 kk122374 Exp $
003     *
004     * Copyright 2002 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.util;
011    
012    import java.io.IOException;
013    import javax.xml.bind.JAXBContext;
014    import javax.xml.bind.JAXBException;
015    import javax.xml.bind.Marshaller;
016    import javax.xml.transform.sax.SAXSource;
017    
018    import org.xml.sax.ContentHandler;
019    import org.xml.sax.DTDHandler;
020    import org.xml.sax.EntityResolver;
021    import org.xml.sax.ErrorHandler;
022    import org.xml.sax.InputSource;
023    import org.xml.sax.SAXException;
024    import org.xml.sax.SAXNotRecognizedException;
025    import org.xml.sax.SAXParseException;
026    import org.xml.sax.XMLReader;
027    import org.xml.sax.ext.LexicalHandler;
028    import org.xml.sax.helpers.XMLFilterImpl;
029    
030    /**
031     * JAXP {@link javax.xml.transform.Source} implementation
032     * that marshals a JAXB-generated object.
033     * 
034     * <p>
035     * This utility class is useful to combine JAXB with
036     * other Java/XML technologies.
037     * 
038     * <p>
039     * The following example shows how to use JAXB to marshal a document
040     * for transformation by XSLT.
041     * 
042     * <blockquote>
043     *    <pre>
044     *       MyObject o = // get JAXB content tree
045     *       
046     *       // jaxbContext is a JAXBContext object from which 'o' is created.
047     *       JAXBSource source = new JAXBSource( jaxbContext, o );
048     *       
049     *       // set up XSLT transformation
050     *       TransformerFactory tf = TransformerFactory.newInstance();
051     *       Transformer t = tf.newTransformer(new StreamSource("test.xsl"));
052     *       
053     *       // run transformation
054     *       t.transform(source,new StreamResult(System.out));
055     *    </pre>
056     * </blockquote>
057     * 
058     * @author
059     *      Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com)
060     */
061    public class JAXBSource extends SAXSource {
062    
063        /**
064         * Creates a new {@link javax.xml.transform.Source} for the given content object.
065         * 
066         * @param   context
067         *      JAXBContext that was used to create
068         *      <code>contentObject</code>. This context is used
069         *      to create a new instance of marshaller and must not be null.
070         * @param   contentObject
071         *      An instance of a JAXB-generated class, which will be
072         *      used as a {@link javax.xml.transform.Source} (by marshalling it into XML).  It must
073         *      not be null.
074         * @throws JAXBException if an error is encountered while creating the
075         * JAXBSource or if either of the parameters are null.
076         */
077        public JAXBSource( JAXBContext context, Object contentObject ) 
078            throws JAXBException {
079                
080            this( 
081                ( context == null ) ? 
082                    assertionFailed( Messages.format( Messages.SOURCE_NULL_CONTEXT ) ) : 
083                    context.createMarshaller(),
084                    
085                ( contentObject == null ) ? 
086                    assertionFailed( Messages.format( Messages.SOURCE_NULL_CONTENT ) ) : 
087                    contentObject);
088        }
089        
090        /**
091         * Creates a new {@link javax.xml.transform.Source} for the given content object.
092         * 
093         * @param   marshaller
094         *      A marshaller instance that will be used to marshal
095         *      <code>contentObject</code> into XML. This must be
096         *      created from a JAXBContext that was used to build
097         *      <code>contentObject</code> and must not be null.
098         * @param   contentObject
099         *      An instance of a JAXB-generated class, which will be
100         *      used as a {@link javax.xml.transform.Source} (by marshalling it into XML).  It must
101         *      not be null.
102         * @throws JAXBException if an error is encountered while creating the
103         * JAXBSource or if either of the parameters are null.
104         */
105        public JAXBSource( Marshaller marshaller, Object contentObject ) 
106            throws JAXBException {
107                
108            if( marshaller == null )
109                throw new JAXBException( 
110                    Messages.format( Messages.SOURCE_NULL_MARSHALLER ) );
111                    
112            if( contentObject == null )
113                throw new JAXBException( 
114                    Messages.format( Messages.SOURCE_NULL_CONTENT ) );
115                
116            this.marshaller = marshaller;
117            this.contentObject = contentObject;
118            
119            super.setXMLReader(pseudoParser);
120            // pass a dummy InputSource. We don't care
121            super.setInputSource(new InputSource());
122        }
123        
124        private final Marshaller marshaller;
125        private final Object contentObject;
126        
127        // this object will pretend as an XMLReader.
128        // no matter what parameter is specified to the parse method,
129        // it just parse the contentObject.
130        private final XMLReader pseudoParser = new XMLReader() {
131            public boolean getFeature(String name) throws SAXNotRecognizedException {
132                throw new SAXNotRecognizedException(name);
133            }
134    
135            public void setFeature(String name, boolean value) throws SAXNotRecognizedException {
136                throw new SAXNotRecognizedException(name);
137            }
138    
139            public Object getProperty(String name) throws SAXNotRecognizedException {
140                if( "http://xml.org/sax/properties/lexical-handler".equals(name) ) {
141                    return lexicalHandler;
142                }
143                throw new SAXNotRecognizedException(name);
144            }
145    
146            public void setProperty(String name, Object value) throws SAXNotRecognizedException {
147                if( "http://xml.org/sax/properties/lexical-handler".equals(name) ) {
148                    this.lexicalHandler = (LexicalHandler)value;
149                    return;
150                }
151                throw new SAXNotRecognizedException(name);
152            }
153            
154            private LexicalHandler lexicalHandler;
155    
156            // we will store this value but never use it by ourselves.
157            private EntityResolver entityResolver;
158            public void setEntityResolver(EntityResolver resolver) {
159                this.entityResolver = resolver;
160            }
161            public EntityResolver getEntityResolver() {
162                return entityResolver;
163            }
164    
165            private DTDHandler dtdHandler;
166            public void setDTDHandler(DTDHandler handler) {
167                this.dtdHandler = handler;
168            }
169            public DTDHandler getDTDHandler() {
170                return dtdHandler;
171            }
172    
173            // SAX allows ContentHandler to be changed during the parsing,
174            // but JAXB doesn't. So this repeater will sit between those
175            // two components.
176            private XMLFilterImpl repeater = new XMLFilterImpl();
177            
178            public void setContentHandler(ContentHandler handler) {
179                repeater.setContentHandler(handler);
180            }
181            public ContentHandler getContentHandler() {
182                return repeater.getContentHandler();
183            }
184    
185            private ErrorHandler errorHandler;
186            public void setErrorHandler(ErrorHandler handler) {
187                this.errorHandler = handler;
188            }
189            public ErrorHandler getErrorHandler() {
190                return errorHandler;
191            }
192    
193            public void parse(InputSource input) throws IOException, SAXException {
194                parse();
195            }
196    
197            public void parse(String systemId) throws IOException, SAXException {
198                parse();
199            }
200            
201            public void parse() throws SAXException {
202                // parses a content object by using the given marshaller
203                // SAX events will be sent to the repeater, and the repeater
204                // will further forward it to an appropriate component.
205                try {
206                    marshaller.marshal( contentObject, repeater );
207                } catch( JAXBException e ) {
208                    // wrap it to a SAXException
209                    SAXParseException se = 
210                        new SAXParseException( e.getMessage(),
211                            null, null, -1, -1, e );
212                            
213                    // if the consumer sets an error handler, it is our responsibility
214                    // to notify it.
215                    if(errorHandler!=null)
216                        errorHandler.fatalError(se);
217                    
218                    // this is a fatal error. Even if the error handler
219                    // returns, we will abort anyway.
220                    throw se;
221                }
222            }
223        };
224        
225        /**
226         * Hook to throw exception from the middle of a contructor chained call
227         * to this
228         */
229        private static Marshaller assertionFailed( String message ) 
230            throws JAXBException {
231                
232            throw new JAXBException( message );
233        }
234    }