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 
  26 package javax.xml.bind.util;
  27 
  28 import org.xml.sax.ContentHandler;
  29 import org.xml.sax.DTDHandler;
  30 import org.xml.sax.EntityResolver;
  31 import org.xml.sax.ErrorHandler;
  32 import org.xml.sax.InputSource;
  33 import org.xml.sax.SAXException;
  34 import org.xml.sax.SAXNotRecognizedException;
  35 import org.xml.sax.SAXParseException;
  36 import org.xml.sax.XMLReader;
  37 import org.xml.sax.ext.LexicalHandler;
  38 import org.xml.sax.helpers.XMLFilterImpl;
  39 
  40 import javax.xml.bind.JAXBContext;
  41 import javax.xml.bind.JAXBException;
  42 import javax.xml.bind.Marshaller;
  43 import javax.xml.transform.sax.SAXSource;
  44 import 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  */
  92 public 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 }