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