Sun, 31 Aug 2014 16:14:36 +0400
8036981: JAXB not preserving formatting for xsd:any Mixed content
Reviewed-by: lancea, mkos
ohair@286 | 1 | /* |
alanb@368 | 2 | * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. |
ohair@286 | 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
ohair@286 | 4 | * |
ohair@286 | 5 | * This code is free software; you can redistribute it and/or modify it |
ohair@286 | 6 | * under the terms of the GNU General Public License version 2 only, as |
ohair@286 | 7 | * published by the Free Software Foundation. Oracle designates this |
ohair@286 | 8 | * particular file as subject to the "Classpath" exception as provided |
ohair@286 | 9 | * by Oracle in the LICENSE file that accompanied this code. |
ohair@286 | 10 | * |
ohair@286 | 11 | * This code is distributed in the hope that it will be useful, but WITHOUT |
ohair@286 | 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
ohair@286 | 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
ohair@286 | 14 | * version 2 for more details (a copy is included in the LICENSE file that |
ohair@286 | 15 | * accompanied this code). |
ohair@286 | 16 | * |
ohair@286 | 17 | * You should have received a copy of the GNU General Public License version |
ohair@286 | 18 | * 2 along with this work; if not, write to the Free Software Foundation, |
ohair@286 | 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
ohair@286 | 20 | * |
ohair@286 | 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
ohair@286 | 22 | * or visit www.oracle.com if you need additional information or have any |
ohair@286 | 23 | * questions. |
ohair@286 | 24 | */ |
ohair@286 | 25 | |
ohair@286 | 26 | package com.sun.xml.internal.bind.v2.runtime; |
ohair@286 | 27 | |
ohair@286 | 28 | import java.io.BufferedWriter; |
ohair@286 | 29 | import java.io.Closeable; |
ohair@286 | 30 | import java.io.FileOutputStream; |
ohair@286 | 31 | import java.io.Flushable; |
ohair@286 | 32 | import java.io.IOException; |
ohair@286 | 33 | import java.io.OutputStream; |
ohair@286 | 34 | import java.io.OutputStreamWriter; |
ohair@286 | 35 | import java.io.UnsupportedEncodingException; |
ohair@286 | 36 | import java.io.Writer; |
ohair@286 | 37 | |
ohair@286 | 38 | import java.net.URI; |
ohair@286 | 39 | import javax.xml.bind.JAXBException; |
ohair@286 | 40 | import javax.xml.bind.MarshalException; |
ohair@286 | 41 | import javax.xml.bind.Marshaller; |
ohair@286 | 42 | import javax.xml.bind.PropertyException; |
ohair@286 | 43 | import javax.xml.bind.ValidationEvent; |
ohair@286 | 44 | import javax.xml.bind.ValidationEventHandler; |
ohair@286 | 45 | import javax.xml.bind.annotation.adapters.XmlAdapter; |
ohair@286 | 46 | import javax.xml.bind.attachment.AttachmentMarshaller; |
ohair@286 | 47 | import javax.xml.bind.helpers.AbstractMarshallerImpl; |
ohair@286 | 48 | import javax.xml.stream.XMLEventWriter; |
ohair@286 | 49 | import javax.xml.stream.XMLStreamException; |
ohair@286 | 50 | import javax.xml.stream.XMLStreamWriter; |
ohair@286 | 51 | import javax.xml.transform.Result; |
ohair@286 | 52 | import javax.xml.transform.dom.DOMResult; |
ohair@286 | 53 | import javax.xml.transform.sax.SAXResult; |
ohair@286 | 54 | import javax.xml.transform.stream.StreamResult; |
ohair@286 | 55 | import javax.xml.validation.Schema; |
ohair@286 | 56 | import javax.xml.validation.ValidatorHandler; |
ohair@286 | 57 | import javax.xml.namespace.NamespaceContext; |
ohair@286 | 58 | |
ohair@286 | 59 | import com.sun.xml.internal.bind.api.JAXBRIContext; |
ohair@286 | 60 | import com.sun.xml.internal.bind.marshaller.CharacterEscapeHandler; |
ohair@286 | 61 | import com.sun.xml.internal.bind.marshaller.DataWriter; |
ohair@286 | 62 | import com.sun.xml.internal.bind.marshaller.DumbEscapeHandler; |
ohair@286 | 63 | import com.sun.xml.internal.bind.marshaller.MinimumEscapeHandler; |
ohair@286 | 64 | import com.sun.xml.internal.bind.marshaller.NamespacePrefixMapper; |
ohair@286 | 65 | import com.sun.xml.internal.bind.marshaller.NioEscapeHandler; |
ohair@286 | 66 | import com.sun.xml.internal.bind.marshaller.SAX2DOMEx; |
ohair@286 | 67 | import com.sun.xml.internal.bind.marshaller.XMLWriter; |
ohair@286 | 68 | import com.sun.xml.internal.bind.v2.runtime.output.C14nXmlOutput; |
ohair@286 | 69 | import com.sun.xml.internal.bind.v2.runtime.output.Encoded; |
ohair@286 | 70 | import com.sun.xml.internal.bind.v2.runtime.output.ForkXmlOutput; |
ohair@286 | 71 | import com.sun.xml.internal.bind.v2.runtime.output.IndentingUTF8XmlOutput; |
ohair@286 | 72 | import com.sun.xml.internal.bind.v2.runtime.output.NamespaceContextImpl; |
ohair@286 | 73 | import com.sun.xml.internal.bind.v2.runtime.output.SAXOutput; |
ohair@286 | 74 | import com.sun.xml.internal.bind.v2.runtime.output.UTF8XmlOutput; |
ohair@286 | 75 | import com.sun.xml.internal.bind.v2.runtime.output.XMLEventWriterOutput; |
ohair@286 | 76 | import com.sun.xml.internal.bind.v2.runtime.output.XMLStreamWriterOutput; |
ohair@286 | 77 | import com.sun.xml.internal.bind.v2.runtime.output.XmlOutput; |
ohair@286 | 78 | import com.sun.xml.internal.bind.v2.util.FatalAdapter; |
ohair@286 | 79 | |
ohair@286 | 80 | import java.net.URISyntaxException; |
ohair@286 | 81 | import org.w3c.dom.Document; |
ohair@286 | 82 | import org.w3c.dom.Node; |
ohair@286 | 83 | import org.xml.sax.SAXException; |
ohair@286 | 84 | import org.xml.sax.helpers.XMLFilterImpl; |
ohair@286 | 85 | |
ohair@286 | 86 | /** |
ohair@286 | 87 | * Implementation of {@link Marshaller} interface for the JAXB RI. |
ohair@286 | 88 | * |
ohair@286 | 89 | * <p> |
ohair@286 | 90 | * Eventually all the {@link #marshal} methods call into |
ohair@286 | 91 | * the {@link #write} method. |
ohair@286 | 92 | * |
ohair@286 | 93 | * @author Kohsuke Kawaguchi |
ohair@286 | 94 | * @author Vivek Pandey |
ohair@286 | 95 | */ |
ohair@286 | 96 | public /*to make unit tests happy*/ final class MarshallerImpl extends AbstractMarshallerImpl implements ValidationEventHandler |
ohair@286 | 97 | { |
ohair@286 | 98 | /** Indentation string. Default is four whitespaces. */ |
ohair@286 | 99 | private String indent = " "; |
ohair@286 | 100 | |
ohair@286 | 101 | /** Used to assign prefixes to namespace URIs. */ |
ohair@286 | 102 | private NamespacePrefixMapper prefixMapper = null; |
ohair@286 | 103 | |
ohair@286 | 104 | /** Object that handles character escaping. */ |
ohair@286 | 105 | private CharacterEscapeHandler escapeHandler = null; |
ohair@286 | 106 | |
ohair@286 | 107 | /** XML BLOB written after the XML declaration. */ |
ohair@286 | 108 | private String header=null; |
ohair@286 | 109 | |
ohair@286 | 110 | /** reference to the context that created this object */ |
ohair@286 | 111 | final JAXBContextImpl context; |
ohair@286 | 112 | |
ohair@286 | 113 | protected final XMLSerializer serializer; |
ohair@286 | 114 | |
ohair@286 | 115 | /** |
ohair@286 | 116 | * Non-null if we do the marshal-time validation. |
ohair@286 | 117 | */ |
ohair@286 | 118 | private Schema schema; |
ohair@286 | 119 | |
ohair@286 | 120 | /** Marshaller.Listener */ |
ohair@286 | 121 | private Listener externalListener = null; |
ohair@286 | 122 | |
ohair@286 | 123 | /** Configured for c14n? */ |
ohair@286 | 124 | private boolean c14nSupport; |
ohair@286 | 125 | |
ohair@286 | 126 | // while createing XmlOutput those values may be set. |
ohair@286 | 127 | // if these are non-null they need to be cleaned up |
ohair@286 | 128 | private Flushable toBeFlushed; |
ohair@286 | 129 | private Closeable toBeClosed; |
ohair@286 | 130 | |
ohair@286 | 131 | /** |
ohair@286 | 132 | * @param assoc |
ohair@286 | 133 | * non-null if the marshaller is working inside {@link BinderImpl}. |
ohair@286 | 134 | */ |
ohair@286 | 135 | public MarshallerImpl( JAXBContextImpl c, AssociationMap assoc ) { |
ohair@286 | 136 | context = c; |
ohair@286 | 137 | serializer = new XMLSerializer(this); |
ohair@286 | 138 | c14nSupport = context.c14nSupport; |
ohair@286 | 139 | |
ohair@286 | 140 | try { |
ohair@286 | 141 | setEventHandler(this); |
ohair@286 | 142 | } catch (JAXBException e) { |
ohair@286 | 143 | throw new AssertionError(e); // impossible |
ohair@286 | 144 | } |
ohair@286 | 145 | } |
ohair@286 | 146 | |
ohair@286 | 147 | public JAXBContextImpl getContext() { |
ohair@286 | 148 | return context; |
ohair@286 | 149 | } |
ohair@286 | 150 | |
ohair@286 | 151 | /** |
ohair@286 | 152 | * Marshals to {@link OutputStream} with the given in-scope namespaces |
ohair@286 | 153 | * taken into account. |
ohair@286 | 154 | * |
ohair@286 | 155 | * @since 2.1.5 |
ohair@286 | 156 | */ |
ohair@286 | 157 | public void marshal(Object obj, OutputStream out, NamespaceContext inscopeNamespace) throws JAXBException { |
ohair@286 | 158 | write(obj, createWriter(out), new StAXPostInitAction(inscopeNamespace,serializer)); |
ohair@286 | 159 | } |
ohair@286 | 160 | |
ohair@286 | 161 | @Override |
ohair@286 | 162 | public void marshal(Object obj, XMLStreamWriter writer) throws JAXBException { |
ohair@286 | 163 | write(obj, XMLStreamWriterOutput.create(writer,context), new StAXPostInitAction(writer,serializer)); |
ohair@286 | 164 | } |
ohair@286 | 165 | |
ohair@286 | 166 | @Override |
ohair@286 | 167 | public void marshal(Object obj, XMLEventWriter writer) throws JAXBException { |
ohair@286 | 168 | write(obj, new XMLEventWriterOutput(writer), new StAXPostInitAction(writer,serializer)); |
ohair@286 | 169 | } |
ohair@286 | 170 | |
ohair@286 | 171 | public void marshal(Object obj, XmlOutput output) throws JAXBException { |
ohair@286 | 172 | write(obj, output, null ); |
ohair@286 | 173 | } |
ohair@286 | 174 | |
ohair@286 | 175 | /** |
ohair@286 | 176 | * Creates {@link XmlOutput} from the given {@link Result} object. |
ohair@286 | 177 | */ |
ohair@286 | 178 | final XmlOutput createXmlOutput(Result result) throws JAXBException { |
ohair@286 | 179 | if (result instanceof SAXResult) |
ohair@286 | 180 | return new SAXOutput(((SAXResult) result).getHandler()); |
ohair@286 | 181 | |
ohair@286 | 182 | if (result instanceof DOMResult) { |
ohair@286 | 183 | final Node node = ((DOMResult) result).getNode(); |
ohair@286 | 184 | |
ohair@286 | 185 | if (node == null) { |
alanb@368 | 186 | Document doc = JAXBContextImpl.createDom(getContext().disableSecurityProcessing); |
ohair@286 | 187 | ((DOMResult) result).setNode(doc); |
ohair@286 | 188 | return new SAXOutput(new SAX2DOMEx(doc)); |
ohair@286 | 189 | } else { |
ohair@286 | 190 | return new SAXOutput(new SAX2DOMEx(node)); |
ohair@286 | 191 | } |
ohair@286 | 192 | } |
ohair@286 | 193 | if (result instanceof StreamResult) { |
ohair@286 | 194 | StreamResult sr = (StreamResult) result; |
ohair@286 | 195 | |
ohair@286 | 196 | if (sr.getWriter() != null) |
ohair@286 | 197 | return createWriter(sr.getWriter()); |
ohair@286 | 198 | else if (sr.getOutputStream() != null) |
ohair@286 | 199 | return createWriter(sr.getOutputStream()); |
ohair@286 | 200 | else if (sr.getSystemId() != null) { |
ohair@286 | 201 | String fileURL = sr.getSystemId(); |
ohair@286 | 202 | |
ohair@286 | 203 | try { |
ohair@286 | 204 | fileURL = new URI(fileURL).getPath(); |
ohair@286 | 205 | } catch (URISyntaxException use) { |
ohair@286 | 206 | // otherwise assume that it's a file name |
ohair@286 | 207 | } |
ohair@286 | 208 | |
ohair@286 | 209 | try { |
ohair@286 | 210 | FileOutputStream fos = new FileOutputStream(fileURL); |
ohair@286 | 211 | assert toBeClosed==null; |
ohair@286 | 212 | toBeClosed = fos; |
ohair@286 | 213 | return createWriter(fos); |
ohair@286 | 214 | } catch (IOException e) { |
ohair@286 | 215 | throw new MarshalException(e); |
ohair@286 | 216 | } |
ohair@286 | 217 | } |
ohair@286 | 218 | } |
ohair@286 | 219 | |
ohair@286 | 220 | // unsupported parameter type |
ohair@286 | 221 | throw new MarshalException(Messages.UNSUPPORTED_RESULT.format()); |
ohair@286 | 222 | } |
ohair@286 | 223 | |
ohair@286 | 224 | /** |
ohair@286 | 225 | * Creates an appropriate post-init action object. |
ohair@286 | 226 | */ |
ohair@286 | 227 | final Runnable createPostInitAction(Result result) { |
ohair@286 | 228 | if (result instanceof DOMResult) { |
ohair@286 | 229 | Node node = ((DOMResult) result).getNode(); |
ohair@286 | 230 | return new DomPostInitAction(node,serializer); |
ohair@286 | 231 | } |
ohair@286 | 232 | return null; |
ohair@286 | 233 | } |
ohair@286 | 234 | |
ohair@286 | 235 | public void marshal(Object target,Result result) throws JAXBException { |
ohair@286 | 236 | write(target, createXmlOutput(result), createPostInitAction(result)); |
ohair@286 | 237 | } |
ohair@286 | 238 | |
ohair@286 | 239 | |
ohair@286 | 240 | /** |
ohair@286 | 241 | * Used by {@link BridgeImpl} to write an arbitrary object as a fragment. |
ohair@286 | 242 | */ |
ohair@286 | 243 | protected final <T> void write(Name rootTagName, JaxBeanInfo<T> bi, T obj, XmlOutput out,Runnable postInitAction) throws JAXBException { |
ohair@286 | 244 | try { |
ohair@286 | 245 | try { |
ohair@286 | 246 | prewrite(out, true, postInitAction); |
ohair@286 | 247 | serializer.startElement(rootTagName,null); |
ohair@286 | 248 | if(bi.jaxbType==Void.class || bi.jaxbType==void.class) { |
ohair@286 | 249 | // special case for void |
ohair@286 | 250 | serializer.endNamespaceDecls(null); |
ohair@286 | 251 | serializer.endAttributes(); |
ohair@286 | 252 | } else { // normal cases |
ohair@286 | 253 | if(obj==null) |
ohair@286 | 254 | serializer.writeXsiNilTrue(); |
ohair@286 | 255 | else |
ohair@286 | 256 | serializer.childAsXsiType(obj,"root",bi, false); |
ohair@286 | 257 | } |
ohair@286 | 258 | serializer.endElement(); |
ohair@286 | 259 | postwrite(); |
ohair@286 | 260 | } catch( SAXException e ) { |
ohair@286 | 261 | throw new MarshalException(e); |
ohair@286 | 262 | } catch (IOException e) { |
ohair@286 | 263 | throw new MarshalException(e); |
ohair@286 | 264 | } catch (XMLStreamException e) { |
ohair@286 | 265 | throw new MarshalException(e); |
ohair@286 | 266 | } finally { |
ohair@286 | 267 | serializer.close(); |
ohair@286 | 268 | } |
ohair@286 | 269 | } finally { |
ohair@286 | 270 | cleanUp(); |
ohair@286 | 271 | } |
ohair@286 | 272 | } |
ohair@286 | 273 | |
ohair@286 | 274 | /** |
ohair@286 | 275 | * All the marshal method invocation eventually comes down to this call. |
ohair@286 | 276 | */ |
ohair@286 | 277 | private void write(Object obj, XmlOutput out, Runnable postInitAction) throws JAXBException { |
ohair@286 | 278 | try { |
ohair@286 | 279 | if( obj == null ) |
ohair@286 | 280 | throw new IllegalArgumentException(Messages.NOT_MARSHALLABLE.format()); |
ohair@286 | 281 | |
ohair@286 | 282 | if( schema!=null ) { |
ohair@286 | 283 | // send the output to the validator as well |
ohair@286 | 284 | ValidatorHandler validator = schema.newValidatorHandler(); |
ohair@286 | 285 | validator.setErrorHandler(new FatalAdapter(serializer)); |
ohair@286 | 286 | // work around a bug in JAXP validator in Tiger |
ohair@286 | 287 | XMLFilterImpl f = new XMLFilterImpl() { |
ohair@286 | 288 | @Override |
ohair@286 | 289 | public void startPrefixMapping(String prefix, String uri) throws SAXException { |
ohair@286 | 290 | super.startPrefixMapping(prefix.intern(), uri.intern()); |
ohair@286 | 291 | } |
ohair@286 | 292 | }; |
ohair@286 | 293 | f.setContentHandler(validator); |
ohair@286 | 294 | out = new ForkXmlOutput( new SAXOutput(f) { |
ohair@286 | 295 | @Override |
ohair@286 | 296 | public void startDocument(XMLSerializer serializer, boolean fragment, int[] nsUriIndex2prefixIndex, NamespaceContextImpl nsContext) throws SAXException, IOException, XMLStreamException { |
ohair@286 | 297 | super.startDocument(serializer, false, nsUriIndex2prefixIndex, nsContext); |
ohair@286 | 298 | } |
ohair@286 | 299 | @Override |
ohair@286 | 300 | public void endDocument(boolean fragment) throws SAXException, IOException, XMLStreamException { |
ohair@286 | 301 | super.endDocument(false); |
ohair@286 | 302 | } |
ohair@286 | 303 | }, out ); |
ohair@286 | 304 | } |
ohair@286 | 305 | |
ohair@286 | 306 | try { |
ohair@286 | 307 | prewrite(out,isFragment(),postInitAction); |
ohair@286 | 308 | serializer.childAsRoot(obj); |
ohair@286 | 309 | postwrite(); |
ohair@286 | 310 | } catch( SAXException e ) { |
ohair@286 | 311 | throw new MarshalException(e); |
ohair@286 | 312 | } catch (IOException e) { |
ohair@286 | 313 | throw new MarshalException(e); |
ohair@286 | 314 | } catch (XMLStreamException e) { |
ohair@286 | 315 | throw new MarshalException(e); |
ohair@286 | 316 | } finally { |
ohair@286 | 317 | serializer.close(); |
ohair@286 | 318 | } |
ohair@286 | 319 | } finally { |
ohair@286 | 320 | cleanUp(); |
ohair@286 | 321 | } |
ohair@286 | 322 | } |
ohair@286 | 323 | |
ohair@286 | 324 | private void cleanUp() { |
ohair@286 | 325 | if(toBeFlushed!=null) |
ohair@286 | 326 | try { |
ohair@286 | 327 | toBeFlushed.flush(); |
ohair@286 | 328 | } catch (IOException e) { |
ohair@286 | 329 | // ignore |
ohair@286 | 330 | } |
ohair@286 | 331 | if(toBeClosed!=null) |
ohair@286 | 332 | try { |
ohair@286 | 333 | toBeClosed.close(); |
ohair@286 | 334 | } catch (IOException e) { |
ohair@286 | 335 | // ignore |
ohair@286 | 336 | } |
ohair@286 | 337 | toBeFlushed = null; |
ohair@286 | 338 | toBeClosed = null; |
ohair@286 | 339 | } |
ohair@286 | 340 | |
ohair@286 | 341 | // common parts between two write methods. |
ohair@286 | 342 | |
ohair@286 | 343 | private void prewrite(XmlOutput out, boolean fragment, Runnable postInitAction) throws IOException, SAXException, XMLStreamException { |
ohair@286 | 344 | serializer.startDocument(out,fragment,getSchemaLocation(),getNoNSSchemaLocation()); |
ohair@286 | 345 | if(postInitAction!=null) postInitAction.run(); |
ohair@286 | 346 | if(prefixMapper!=null) { |
ohair@286 | 347 | // be defensive as we work with the user's code |
ohair@286 | 348 | String[] decls = prefixMapper.getContextualNamespaceDecls(); |
ohair@286 | 349 | if(decls!=null) { // defensive check |
ohair@286 | 350 | for( int i=0; i<decls.length; i+=2 ) { |
ohair@286 | 351 | String prefix = decls[i]; |
ohair@286 | 352 | String nsUri = decls[i+1]; |
ohair@286 | 353 | if(nsUri!=null && prefix!=null) // defensive check |
ohair@286 | 354 | serializer.addInscopeBinding(nsUri,prefix); |
ohair@286 | 355 | } |
ohair@286 | 356 | } |
ohair@286 | 357 | } |
ohair@286 | 358 | serializer.setPrefixMapper(prefixMapper); |
ohair@286 | 359 | } |
ohair@286 | 360 | |
ohair@286 | 361 | private void postwrite() throws IOException, SAXException, XMLStreamException { |
ohair@286 | 362 | serializer.endDocument(); |
ohair@286 | 363 | serializer.reconcileID(); // extra check |
ohair@286 | 364 | } |
ohair@286 | 365 | |
ohair@286 | 366 | |
ohair@286 | 367 | // |
ohair@286 | 368 | // |
ohair@286 | 369 | // create XMLWriter by specifing various type of output. |
ohair@286 | 370 | // |
ohair@286 | 371 | // |
ohair@286 | 372 | |
ohair@286 | 373 | protected CharacterEscapeHandler createEscapeHandler( String encoding ) { |
ohair@286 | 374 | if( escapeHandler!=null ) |
ohair@286 | 375 | // user-specified one takes precedence. |
ohair@286 | 376 | return escapeHandler; |
ohair@286 | 377 | |
ohair@286 | 378 | if( encoding.startsWith("UTF") ) |
ohair@286 | 379 | // no need for character reference. Use the handler |
ohair@286 | 380 | // optimized for that pattern. |
ohair@286 | 381 | return MinimumEscapeHandler.theInstance; |
ohair@286 | 382 | |
ohair@286 | 383 | // otherwise try to find one from the encoding |
ohair@286 | 384 | try { |
ohair@286 | 385 | // try new JDK1.4 NIO |
ohair@286 | 386 | return new NioEscapeHandler( getJavaEncoding(encoding) ); |
ohair@286 | 387 | } catch( Throwable e ) { |
ohair@286 | 388 | // if that fails, fall back to the dumb mode |
ohair@286 | 389 | return DumbEscapeHandler.theInstance; |
ohair@286 | 390 | } |
ohair@286 | 391 | } |
ohair@286 | 392 | |
ohair@286 | 393 | public XmlOutput createWriter( Writer w, String encoding ) { |
ohair@286 | 394 | // XMLWriter doesn't do buffering, so do it here if it looks like a good idea |
ohair@286 | 395 | if(!(w instanceof BufferedWriter)) |
ohair@286 | 396 | w = new BufferedWriter(w); |
ohair@286 | 397 | |
ohair@286 | 398 | assert toBeFlushed==null; |
ohair@286 | 399 | toBeFlushed = w; |
ohair@286 | 400 | |
ohair@286 | 401 | CharacterEscapeHandler ceh = createEscapeHandler(encoding); |
ohair@286 | 402 | XMLWriter xw; |
ohair@286 | 403 | |
ohair@286 | 404 | if(isFormattedOutput()) { |
ohair@286 | 405 | DataWriter d = new DataWriter(w,encoding,ceh); |
ohair@286 | 406 | d.setIndentStep(indent); |
ohair@286 | 407 | xw=d; |
ohair@286 | 408 | } else |
ohair@286 | 409 | xw = new XMLWriter(w,encoding,ceh); |
ohair@286 | 410 | |
ohair@286 | 411 | xw.setXmlDecl(!isFragment()); |
ohair@286 | 412 | xw.setHeader(header); |
ohair@286 | 413 | return new SAXOutput(xw); // TODO: don't we need a better writer? |
ohair@286 | 414 | } |
ohair@286 | 415 | |
ohair@286 | 416 | public XmlOutput createWriter(Writer w) { |
ohair@286 | 417 | return createWriter(w, getEncoding()); |
ohair@286 | 418 | } |
ohair@286 | 419 | |
ohair@286 | 420 | public XmlOutput createWriter( OutputStream os ) throws JAXBException { |
ohair@286 | 421 | return createWriter(os, getEncoding()); |
ohair@286 | 422 | } |
ohair@286 | 423 | |
ohair@286 | 424 | public XmlOutput createWriter( OutputStream os, String encoding ) throws JAXBException { |
ohair@286 | 425 | // UTF8XmlOutput does buffering on its own, and |
ohair@286 | 426 | // otherwise createWriter(Writer) inserts a buffering, |
ohair@286 | 427 | // so no point in doing a buffering here. |
ohair@286 | 428 | |
ohair@286 | 429 | if(encoding.equals("UTF-8")) { |
ohair@286 | 430 | Encoded[] table = context.getUTF8NameTable(); |
ohair@286 | 431 | final UTF8XmlOutput out; |
ohair@286 | 432 | if(isFormattedOutput()) |
ohair@286 | 433 | out = new IndentingUTF8XmlOutput(os, indent, table, escapeHandler); |
ohair@286 | 434 | else { |
ohair@286 | 435 | if(c14nSupport) |
ohair@286 | 436 | out = new C14nXmlOutput(os, table, context.c14nSupport, escapeHandler); |
ohair@286 | 437 | else |
ohair@286 | 438 | out = new UTF8XmlOutput(os, table, escapeHandler); |
ohair@286 | 439 | } |
ohair@286 | 440 | if(header!=null) |
ohair@286 | 441 | out.setHeader(header); |
ohair@286 | 442 | return out; |
ohair@286 | 443 | } |
ohair@286 | 444 | |
ohair@286 | 445 | try { |
ohair@286 | 446 | return createWriter( |
ohair@286 | 447 | new OutputStreamWriter(os,getJavaEncoding(encoding)), |
ohair@286 | 448 | encoding ); |
ohair@286 | 449 | } catch( UnsupportedEncodingException e ) { |
ohair@286 | 450 | throw new MarshalException( |
ohair@286 | 451 | Messages.UNSUPPORTED_ENCODING.format(encoding), |
ohair@286 | 452 | e ); |
ohair@286 | 453 | } |
ohair@286 | 454 | } |
ohair@286 | 455 | |
ohair@286 | 456 | |
ohair@286 | 457 | @Override |
ohair@286 | 458 | public Object getProperty(String name) throws PropertyException { |
ohair@286 | 459 | if( INDENT_STRING.equals(name) ) |
ohair@286 | 460 | return indent; |
ohair@286 | 461 | if( ENCODING_HANDLER.equals(name) || ENCODING_HANDLER2.equals(name) ) |
ohair@286 | 462 | return escapeHandler; |
ohair@286 | 463 | if( PREFIX_MAPPER.equals(name) ) |
ohair@286 | 464 | return prefixMapper; |
ohair@286 | 465 | if( XMLDECLARATION.equals(name) ) |
ohair@286 | 466 | return !isFragment(); |
ohair@286 | 467 | if( XML_HEADERS.equals(name) ) |
ohair@286 | 468 | return header; |
ohair@286 | 469 | if( C14N.equals(name) ) |
ohair@286 | 470 | return c14nSupport; |
ohair@286 | 471 | if ( OBJECT_IDENTITY_CYCLE_DETECTION.equals(name)) |
ohair@286 | 472 | return serializer.getObjectIdentityCycleDetection(); |
ohair@286 | 473 | |
ohair@286 | 474 | return super.getProperty(name); |
ohair@286 | 475 | } |
ohair@286 | 476 | |
ohair@286 | 477 | @Override |
ohair@286 | 478 | public void setProperty(String name, Object value) throws PropertyException { |
ohair@286 | 479 | if( INDENT_STRING.equals(name) ) { |
ohair@286 | 480 | checkString(name, value); |
ohair@286 | 481 | indent = (String)value; |
ohair@286 | 482 | return; |
ohair@286 | 483 | } |
ohair@286 | 484 | if( ENCODING_HANDLER.equals(name) || ENCODING_HANDLER2.equals(name)) { |
ohair@286 | 485 | if(!(value instanceof CharacterEscapeHandler)) |
ohair@286 | 486 | throw new PropertyException( |
ohair@286 | 487 | Messages.MUST_BE_X.format( |
ohair@286 | 488 | name, |
ohair@286 | 489 | CharacterEscapeHandler.class.getName(), |
ohair@286 | 490 | value.getClass().getName() ) ); |
ohair@286 | 491 | escapeHandler = (CharacterEscapeHandler)value; |
ohair@286 | 492 | return; |
ohair@286 | 493 | } |
ohair@286 | 494 | if( PREFIX_MAPPER.equals(name) ) { |
ohair@286 | 495 | if(!(value instanceof NamespacePrefixMapper)) |
ohair@286 | 496 | throw new PropertyException( |
ohair@286 | 497 | Messages.MUST_BE_X.format( |
ohair@286 | 498 | name, |
ohair@286 | 499 | NamespacePrefixMapper.class.getName(), |
ohair@286 | 500 | value.getClass().getName() ) ); |
ohair@286 | 501 | prefixMapper = (NamespacePrefixMapper)value; |
ohair@286 | 502 | return; |
ohair@286 | 503 | } |
ohair@286 | 504 | if( XMLDECLARATION.equals(name) ) { |
ohair@286 | 505 | checkBoolean(name, value); |
ohair@286 | 506 | // com.sun.xml.internal.bind.xmlDeclaration is an alias for JAXB_FRAGMENT |
ohair@286 | 507 | // setting it to false is treated the same as setting fragment to true. |
ohair@286 | 508 | super.setProperty(JAXB_FRAGMENT, !(Boolean)value); |
ohair@286 | 509 | return; |
ohair@286 | 510 | } |
ohair@286 | 511 | if( XML_HEADERS.equals(name) ) { |
ohair@286 | 512 | checkString(name, value); |
ohair@286 | 513 | header = (String)value; |
ohair@286 | 514 | return; |
ohair@286 | 515 | } |
ohair@286 | 516 | if( C14N.equals(name) ) { |
ohair@286 | 517 | checkBoolean(name,value); |
ohair@286 | 518 | c14nSupport = (Boolean)value; |
ohair@286 | 519 | return; |
ohair@286 | 520 | } |
ohair@286 | 521 | if (OBJECT_IDENTITY_CYCLE_DETECTION.equals(name)) { |
ohair@286 | 522 | checkBoolean(name,value); |
ohair@286 | 523 | serializer.setObjectIdentityCycleDetection((Boolean)value); |
ohair@286 | 524 | return; |
ohair@286 | 525 | } |
ohair@286 | 526 | |
ohair@286 | 527 | super.setProperty(name, value); |
ohair@286 | 528 | } |
ohair@286 | 529 | |
ohair@286 | 530 | /* |
ohair@286 | 531 | * assert that the given object is a Boolean |
ohair@286 | 532 | */ |
ohair@286 | 533 | private void checkBoolean( String name, Object value ) throws PropertyException { |
ohair@286 | 534 | if(!(value instanceof Boolean)) |
ohair@286 | 535 | throw new PropertyException( |
ohair@286 | 536 | Messages.MUST_BE_X.format( |
ohair@286 | 537 | name, |
ohair@286 | 538 | Boolean.class.getName(), |
ohair@286 | 539 | value.getClass().getName() ) ); |
ohair@286 | 540 | } |
ohair@286 | 541 | |
ohair@286 | 542 | /* |
ohair@286 | 543 | * assert that the given object is a String |
ohair@286 | 544 | */ |
ohair@286 | 545 | private void checkString( String name, Object value ) throws PropertyException { |
ohair@286 | 546 | if(!(value instanceof String)) |
ohair@286 | 547 | throw new PropertyException( |
ohair@286 | 548 | Messages.MUST_BE_X.format( |
ohair@286 | 549 | name, |
ohair@286 | 550 | String.class.getName(), |
ohair@286 | 551 | value.getClass().getName() ) ); |
ohair@286 | 552 | } |
ohair@286 | 553 | |
ohair@286 | 554 | @Override |
ohair@286 | 555 | public <A extends XmlAdapter> void setAdapter(Class<A> type, A adapter) { |
ohair@286 | 556 | if(type==null) |
ohair@286 | 557 | throw new IllegalArgumentException(); |
ohair@286 | 558 | serializer.putAdapter(type,adapter); |
ohair@286 | 559 | } |
ohair@286 | 560 | |
ohair@286 | 561 | @Override |
ohair@286 | 562 | public <A extends XmlAdapter> A getAdapter(Class<A> type) { |
ohair@286 | 563 | if(type==null) |
ohair@286 | 564 | throw new IllegalArgumentException(); |
ohair@286 | 565 | if(serializer.containsAdapter(type)) |
ohair@286 | 566 | // so as not to create a new instance when this method is called |
ohair@286 | 567 | return serializer.getAdapter(type); |
ohair@286 | 568 | else |
ohair@286 | 569 | return null; |
ohair@286 | 570 | } |
ohair@286 | 571 | |
ohair@286 | 572 | @Override |
ohair@286 | 573 | public void setAttachmentMarshaller(AttachmentMarshaller am) { |
ohair@286 | 574 | serializer.attachmentMarshaller = am; |
ohair@286 | 575 | } |
ohair@286 | 576 | |
ohair@286 | 577 | @Override |
ohair@286 | 578 | public AttachmentMarshaller getAttachmentMarshaller() { |
ohair@286 | 579 | return serializer.attachmentMarshaller; |
ohair@286 | 580 | } |
ohair@286 | 581 | |
ohair@286 | 582 | @Override |
ohair@286 | 583 | public Schema getSchema() { |
ohair@286 | 584 | return schema; |
ohair@286 | 585 | } |
ohair@286 | 586 | |
ohair@286 | 587 | @Override |
ohair@286 | 588 | public void setSchema(Schema s) { |
ohair@286 | 589 | this.schema = s; |
ohair@286 | 590 | } |
ohair@286 | 591 | |
ohair@286 | 592 | /** |
ohair@286 | 593 | * Default error handling behavior fot {@link Marshaller}. |
ohair@286 | 594 | */ |
ohair@286 | 595 | public boolean handleEvent(ValidationEvent event) { |
ohair@286 | 596 | // draconian by default |
ohair@286 | 597 | return false; |
ohair@286 | 598 | } |
ohair@286 | 599 | |
ohair@286 | 600 | @Override |
ohair@286 | 601 | public Listener getListener() { |
ohair@286 | 602 | return externalListener; |
ohair@286 | 603 | } |
ohair@286 | 604 | |
ohair@286 | 605 | @Override |
ohair@286 | 606 | public void setListener(Listener listener) { |
ohair@286 | 607 | externalListener = listener; |
ohair@286 | 608 | } |
ohair@286 | 609 | |
ohair@286 | 610 | // features supported |
ohair@286 | 611 | protected static final String INDENT_STRING = "com.sun.xml.internal.bind.indentString"; |
ohair@286 | 612 | protected static final String PREFIX_MAPPER = "com.sun.xml.internal.bind.namespacePrefixMapper"; |
ohair@286 | 613 | protected static final String ENCODING_HANDLER = "com.sun.xml.internal.bind.characterEscapeHandler"; |
ohair@286 | 614 | protected static final String ENCODING_HANDLER2 = "com.sun.xml.internal.bind.marshaller.CharacterEscapeHandler"; |
ohair@286 | 615 | protected static final String XMLDECLARATION = "com.sun.xml.internal.bind.xmlDeclaration"; |
ohair@286 | 616 | protected static final String XML_HEADERS = "com.sun.xml.internal.bind.xmlHeaders"; |
ohair@286 | 617 | protected static final String C14N = JAXBRIContext.CANONICALIZATION_SUPPORT; |
ohair@286 | 618 | protected static final String OBJECT_IDENTITY_CYCLE_DETECTION = "com.sun.xml.internal.bind.objectIdentitityCycleDetection"; |
ohair@286 | 619 | } |