src/share/jaxws_classes/com/sun/xml/internal/bind/v2/runtime/MarshallerImpl.java

Thu, 31 Aug 2017 15:18:52 +0800

author
aoqi
date
Thu, 31 Aug 2017 15:18:52 +0800
changeset 637
9c07ef4934dd
parent 368
0989ad8c0860
parent 0
373ffda63c9a
child 1546
dc8316632248
permissions
-rw-r--r--

merge

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

mercurial