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

Sun, 18 Jun 2017 23:18:45 +0100

author
aefimov
date
Sun, 18 Jun 2017 23:18:45 +0100
changeset 1443
dffc222439a1
parent 397
b99d7e355d4b
child 637
9c07ef4934dd
permissions
-rw-r--r--

8172297: In java 8, the marshalling with JAX-WS does not escape carriage return
Reviewed-by: lancea

     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.IOException;
    29 import java.lang.reflect.Method;
    30 import java.util.HashSet;
    31 import java.util.Map;
    32 import java.util.Set;
    34 import javax.activation.MimeType;
    35 import javax.xml.bind.DatatypeConverter;
    36 import javax.xml.bind.JAXBException;
    37 import javax.xml.bind.Marshaller;
    38 import javax.xml.bind.ValidationEvent;
    39 import javax.xml.bind.ValidationEventHandler;
    40 import javax.xml.bind.ValidationEventLocator;
    41 import javax.xml.bind.annotation.DomHandler;
    42 import javax.xml.bind.annotation.XmlNs;
    43 import javax.xml.bind.attachment.AttachmentMarshaller;
    44 import javax.xml.bind.helpers.NotIdentifiableEventImpl;
    45 import javax.xml.bind.helpers.ValidationEventImpl;
    46 import javax.xml.bind.helpers.ValidationEventLocatorImpl;
    47 import javax.xml.namespace.QName;
    48 import javax.xml.stream.XMLStreamException;
    49 import javax.xml.transform.Source;
    50 import javax.xml.transform.Transformer;
    51 import javax.xml.transform.TransformerException;
    52 import javax.xml.transform.sax.SAXResult;
    54 import com.sun.istack.internal.SAXException2;
    55 import com.sun.xml.internal.bind.CycleRecoverable;
    56 import com.sun.xml.internal.bind.api.AccessorException;
    57 import com.sun.xml.internal.bind.marshaller.NamespacePrefixMapper;
    58 import com.sun.xml.internal.bind.util.ValidationEventLocatorExImpl;
    59 import com.sun.xml.internal.bind.v2.WellKnownNamespace;
    60 import com.sun.xml.internal.bind.v2.model.runtime.RuntimeBuiltinLeafInfo;
    61 import com.sun.xml.internal.bind.v2.runtime.output.MTOMXmlOutput;
    62 import com.sun.xml.internal.bind.v2.runtime.output.NamespaceContextImpl;
    63 import com.sun.xml.internal.bind.v2.runtime.output.Pcdata;
    64 import com.sun.xml.internal.bind.v2.runtime.output.XmlOutput;
    65 import com.sun.xml.internal.bind.v2.runtime.property.Property;
    66 import com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data;
    67 import com.sun.xml.internal.bind.v2.runtime.unmarshaller.IntData;
    68 import com.sun.xml.internal.bind.v2.util.CollisionCheckStack;
    70 import org.xml.sax.SAXException;
    72 /**
    73  * Receives XML serialization event and writes to {@link XmlOutput}.
    74  *
    75  * <p>
    76  * This object coordinates the overall marshalling efforts across different
    77  * content-tree objects and different target formats.
    78  *
    79  * <p>
    80  * The following CFG gives the proper sequence of method invocation.
    81  *
    82  * <pre>
    83  * MARSHALLING  :=  ELEMENT
    84  * ELEMENT      :=  "startElement" NSDECL* "endNamespaceDecls"
    85  *                        ATTRIBUTE* "endAttributes" BODY "endElement"
    86  *
    87  * NSDECL       :=  "declareNamespace"
    88  *
    89  * ATTRIBUTE    :=  "attribute"
    90  * ATTVALUES    :=  "text"*
    91  *
    92  *
    93  * BODY         :=  ( "text" | ELEMENT )*
    94  * </pre>
    95  *
    96  * <p>
    97  * A marshalling of one element consists of two stages. The first stage is
    98  * for marshalling attributes and collecting namespace declarations.
    99  * The second stage is for marshalling characters/child elements of that element.
   100  *
   101  * <p>
   102  * Observe that multiple invocation of "text" is allowed.
   103  *
   104  * <p>
   105  * Also observe that the namespace declarations are allowed only between
   106  * "startElement" and "endAttributes".
   107  *
   108  * <h2>Exceptions in marshaller</h2>
   109  * <p>
   110  * {@link IOException}, {@link SAXException}, and {@link XMLStreamException}
   111  * are thrown from {@link XmlOutput}. They are always considered fatal, and
   112  * therefore caught only by {@link MarshallerImpl}.
   113  * <p>
   114  * {@link AccessorException} can be thrown when an access to a property/field
   115  * fails, and this is considered as a recoverable error, so it's caught everywhere.
   116  *
   117  * @author  Kohsuke Kawaguchi
   118  */
   119 public final class XMLSerializer extends Coordinator {
   120     public final JAXBContextImpl grammar;
   122     /** The XML printer. */
   123     private XmlOutput out;
   125     public final NameList nameList;
   127     public final int[] knownUri2prefixIndexMap;
   129     private final NamespaceContextImpl nsContext;
   131     private NamespaceContextImpl.Element nse;
   133     // Introduced based on Jersey requirements - to be able to retrieve marshalled name
   134     ThreadLocal<Property> currentProperty = new ThreadLocal<Property>();
   136     /**
   137      * Set to true if a text is already written,
   138      * and we need to print ' ' for additional text methods.
   139      */
   140     private boolean textHasAlreadyPrinted = false;
   142     /**
   143      * Set to false once we see the start tag of the root element.
   144      */
   145     private boolean seenRoot = false;
   147     /** Marshaller object to which this object belongs. */
   148     private final MarshallerImpl marshaller;
   150     /** Objects referenced through IDREF. */
   151     private final Set<Object> idReferencedObjects = new HashSet<Object>();
   153     /** Objects with ID. */
   154     private final Set<Object> objectsWithId = new HashSet<Object>();
   156     /**
   157      * Used to detect cycles in the object.
   158      * Also used to learn what's being marshalled.
   159      */
   160     private final CollisionCheckStack<Object> cycleDetectionStack = new CollisionCheckStack<Object>();
   162     /** Optional attributes to go with root element. */
   163     private String schemaLocation;
   164     private String noNsSchemaLocation;
   166     /** Lazily created identitiy transformer. */
   167     private Transformer identityTransformer;
   169     /** Lazily created. */
   170     private ContentHandlerAdaptor contentHandlerAdapter;
   172     private boolean fragment;
   174     /**
   175      * Cached instance of {@link Base64Data}.
   176      */
   177     private Base64Data base64Data;
   179     /**
   180      * Cached instance of {@link IntData}.
   181      */
   182     private final IntData intData = new IntData();
   184     public AttachmentMarshaller attachmentMarshaller;
   186     /*package*/ XMLSerializer( MarshallerImpl _owner ) {
   187         this.marshaller = _owner;
   188         this.grammar = marshaller.context;
   189         nsContext = new NamespaceContextImpl(this);
   190         nameList = marshaller.context.nameList;
   191         knownUri2prefixIndexMap = new int[nameList.namespaceURIs.length];
   192     }
   194     /**
   195      * Gets the cached instance of {@link Base64Data}.
   196      *
   197      * @deprecated
   198      *      {@link Base64Data} is no longer cached, so that
   199      *      XMLStreamWriterEx impl can retain the data, like JAX-WS does.
   200      */
   201     public Base64Data getCachedBase64DataInstance() {
   202         return new Base64Data();
   203     }
   205     /**
   206      * Gets the ID value from an identifiable object.
   207      */
   208     private String getIdFromObject(Object identifiableObject) throws SAXException, JAXBException {
   209         return grammar.getBeanInfo(identifiableObject,true).getId(identifiableObject,this);
   210     }
   212     private void handleMissingObjectError(String fieldName) throws SAXException, IOException, XMLStreamException {
   213         reportMissingObjectError(fieldName);
   214         // as a marshaller, we should be robust, so we'll continue to marshal
   215         // this document by skipping this missing object.
   216         endNamespaceDecls(null);
   217         endAttributes();
   218     }
   221     public void reportError( ValidationEvent ve ) throws SAXException {
   222         ValidationEventHandler handler;
   224         try {
   225             handler = marshaller.getEventHandler();
   226         } catch( JAXBException e ) {
   227             throw new SAXException2(e);
   228         }
   230         if(!handler.handleEvent(ve)) {
   231             if(ve.getLinkedException() instanceof Exception)
   232                 throw new SAXException2((Exception)ve.getLinkedException());
   233             else
   234                 throw new SAXException2(ve.getMessage());
   235         }
   236     }
   238     /**
   239      * Report an error found as an exception.
   240      *
   241      * @param fieldName
   242      *      the name of the property being processed when an error is found.
   243      */
   244     public final void reportError(String fieldName, Throwable t) throws SAXException {
   245         ValidationEvent ve = new ValidationEventImpl(ValidationEvent.ERROR,
   246             t.getMessage(), getCurrentLocation(fieldName), t);
   247         reportError(ve);
   248     }
   250     public void startElement(Name tagName, Object outerPeer) {
   251         startElement();
   252         nse.setTagName(tagName,outerPeer);
   253     }
   255     public void startElement(String nsUri, String localName, String preferredPrefix, Object outerPeer) {
   256         startElement();
   257         int idx = nsContext.declareNsUri(nsUri, preferredPrefix, false);
   258         nse.setTagName(idx,localName,outerPeer);
   259     }
   261     /**
   262      * Variation of {@link #startElement(String, String, String, Object)} that forces
   263      * a specific prefix. Needed to preserve the prefix when marshalling DOM.
   264      */
   265     public void startElementForce(String nsUri, String localName, String forcedPrefix, Object outerPeer) {
   266         startElement();
   267         int idx = nsContext.force(nsUri, forcedPrefix);
   268         nse.setTagName(idx,localName,outerPeer);
   269     }
   271     public void endNamespaceDecls(Object innerPeer) throws IOException, XMLStreamException {
   272         nsContext.collectionMode = false;
   273         nse.startElement(out,innerPeer);
   274     }
   276     /**
   277      * Switches to the "marshal child texts/elements" mode.
   278      * This method has to be called after the 1st pass is completed.
   279      */
   280     public void endAttributes() throws SAXException, IOException, XMLStreamException  {
   281         if(!seenRoot) {
   282             seenRoot = true;
   283             if(schemaLocation!=null || noNsSchemaLocation!=null) {
   284                 int p = nsContext.getPrefixIndex(WellKnownNamespace.XML_SCHEMA_INSTANCE);
   285                 if(schemaLocation!=null)
   286                     out.attribute(p,"schemaLocation",schemaLocation);
   287                 if(noNsSchemaLocation!=null)
   288                     out.attribute(p,"noNamespaceSchemaLocation",noNsSchemaLocation);
   289             }
   290         }
   292         out.endStartTag();
   293     }
   295     /**
   296      * Ends marshalling of an element.
   297      * Pops the internal stack.
   298      */
   299     public void endElement() throws SAXException, IOException, XMLStreamException {
   300         nse.endElement(out);
   301         nse = nse.pop();
   302         textHasAlreadyPrinted = false;
   303     }
   305     public void leafElement( Name tagName, String data, String fieldName ) throws SAXException, IOException, XMLStreamException {
   306         if(seenRoot) {
   307             textHasAlreadyPrinted = false;
   308             nse = nse.push();
   309             out.beginStartTag(tagName);
   310             out.endStartTag();
   311             if(data != null)
   312                 try {
   313                         out.text(data,false);
   314                 } catch (IllegalArgumentException e) {
   315                     throw new IllegalArgumentException(Messages.ILLEGAL_CONTENT.format(fieldName, e.getMessage()));
   316                 }
   317             out.endTag(tagName);
   318             nse = nse.pop();
   319         } else {
   320             // root element has additional processing like xsi:schemaLocation,
   321             // so we need to go the slow way
   322             startElement(tagName,null);
   323             endNamespaceDecls(null);
   324             endAttributes();
   325                 try {
   326                     out.text(data, false);
   327                 } catch (IllegalArgumentException e) {
   328                     throw new IllegalArgumentException(Messages.ILLEGAL_CONTENT.format(fieldName, e.getMessage()));
   329                 }
   330             endElement();
   331         }
   332     }
   334     public void leafElement( Name tagName, Pcdata data, String fieldName ) throws SAXException, IOException, XMLStreamException {
   335         if(seenRoot) {
   336             textHasAlreadyPrinted = false;
   337             nse = nse.push();
   338             out.beginStartTag(tagName);
   339             out.endStartTag();
   340             if(data != null)
   341                 out.text(data,false);
   342             out.endTag(tagName);
   343             nse = nse.pop();
   344         } else {
   345             // root element has additional processing like xsi:schemaLocation,
   346             // so we need to go the slow way
   347             startElement(tagName,null);
   348             endNamespaceDecls(null);
   349             endAttributes();
   350             out.text(data,false);
   351             endElement();
   352         }
   353     }
   355     public void leafElement( Name tagName, int data, String fieldName ) throws SAXException, IOException, XMLStreamException {
   356         intData.reset(data);
   357         leafElement(tagName,intData,fieldName);
   358     }
   360     /**
   361      * Marshalls text.
   362      *
   363      * <p>
   364      * This method can be called after the {@link #endAttributes()}
   365      * method to marshal texts inside elements.
   366      * If the method is called more than once, those texts are considered
   367      * as separated by whitespaces. For example,
   368      *
   369      * <pre>
   370      * c.startElement("","foo");
   371      * c.endAttributes();
   372      * c.text("abc");
   373      * c.text("def");
   374      *   c.startElement("","bar");
   375      *   c.endAttributes();
   376      *   c.endElement();
   377      * c.text("ghi");
   378      * c.endElement();
   379      * </pre>
   380      *
   381      * will generate <code>&lt;foo>abc def&lt;bar/>ghi&lt;/foo></code>.
   382      */
   383     public void text( String text, String fieldName ) throws SAXException, IOException, XMLStreamException {
   384         // If the assertion fails, it must be a bug of xjc.
   385         // right now, we are not expecting the text method to be called.
   386         if(text==null) {
   387             reportMissingObjectError(fieldName);
   388             return;
   389         }
   391         out.text(text,textHasAlreadyPrinted);
   392         textHasAlreadyPrinted = true;
   393     }
   395     /**
   396      * The {@link #text(String, String)} method that takes {@link Pcdata}.
   397      */
   398     public void text( Pcdata text, String fieldName ) throws SAXException, IOException, XMLStreamException {
   399         // If the assertion fails, it must be a bug of xjc.
   400         // right now, we are not expecting the text method to be called.
   401         if(text==null) {
   402             reportMissingObjectError(fieldName);
   403             return;
   404         }
   406         out.text(text,textHasAlreadyPrinted);
   407         textHasAlreadyPrinted = true;
   408     }
   410     public void attribute(String uri, String local, String value) throws SAXException {
   411         int prefix;
   412         if(uri.length()==0) {
   413             // default namespace. don't need prefix
   414             prefix = -1;
   415         } else {
   416             prefix = nsContext.getPrefixIndex(uri);
   417         }
   419         try {
   420             out.attribute(prefix,local,value);
   421         } catch (IOException e) {
   422             throw new SAXException2(e);
   423         } catch (XMLStreamException e) {
   424             throw new SAXException2(e);
   425         }
   426     }
   428     public void attribute(Name name, CharSequence value) throws IOException, XMLStreamException {
   429         // TODO: consider having the version that takes Pcdata.
   430         // it's common for an element to have int attributes
   431         out.attribute(name,value.toString());
   432     }
   434     public NamespaceContext2 getNamespaceContext() {
   435         return nsContext;
   436     }
   439     public String onID( Object owner, String value ) {
   440         objectsWithId.add(owner);
   441         return value;
   442     }
   444     public String onIDREF( Object obj ) throws SAXException {
   445         String id;
   446         try {
   447             id = getIdFromObject(obj);
   448         } catch (JAXBException e) {
   449             reportError(null,e);
   450             return null; // recover by returning null
   451         }
   452         idReferencedObjects.add(obj);
   453         if(id==null) {
   454             reportError( new NotIdentifiableEventImpl(
   455                 ValidationEvent.ERROR,
   456                 Messages.NOT_IDENTIFIABLE.format(),
   457                 new ValidationEventLocatorImpl(obj) ) );
   458         }
   459         return id;
   460     }
   463     // TODO: think about the exception handling.
   464     // I suppose we don't want to use SAXException. -kk
   466     public void childAsRoot(Object obj) throws JAXBException, IOException, SAXException, XMLStreamException {
   467         final JaxBeanInfo beanInfo = grammar.getBeanInfo(obj, true);
   469         // since the same object will be reported to childAsRoot or
   470         // childAsXsiType, don't make it a part of the collision check.
   471         // but we do need to push it so that getXMIMEContentType will work.
   472         cycleDetectionStack.pushNocheck(obj);
   474         final boolean lookForLifecycleMethods = beanInfo.lookForLifecycleMethods();
   475         if (lookForLifecycleMethods) {
   476             fireBeforeMarshalEvents(beanInfo, obj);
   477         }
   479         beanInfo.serializeRoot(obj,this);
   481         if (lookForLifecycleMethods) {
   482             fireAfterMarshalEvents(beanInfo, obj);
   483         }
   485         cycleDetectionStack.pop();
   486     }
   488     /**
   489      * Pushes the object to {@link #cycleDetectionStack} and also
   490      * detect any cycles.
   491      *
   492      * When a cycle is found, this method tries to recover from it.
   493      *
   494      * @return
   495      *      the object that should be marshalled instead of the given <tt>obj</tt>,
   496      *      or null if the error is found and we need to avoid marshalling this object
   497      *      to prevent infinite recursion. When this method returns null, the error
   498      *      has already been reported.
   499      */
   500     private Object pushObject(Object obj, String fieldName) throws SAXException {
   501         if(!cycleDetectionStack.push(obj))
   502             return obj;
   504         // allow the object to nominate its replacement
   505         if(obj instanceof CycleRecoverable) {
   506             obj = ((CycleRecoverable)obj).onCycleDetected(new CycleRecoverable.Context(){
   507                 public Marshaller getMarshaller() {
   508                     return marshaller;
   509                 }
   510             });
   511             if(obj!=null) {
   512                 // object nominated its replacement.
   513                 // we still need to make sure that the nominated.
   514                 // this may cause inifinite recursion on its own.
   515                 cycleDetectionStack.pop();
   516                 return pushObject(obj,fieldName);
   517             } else
   518                 return null;
   519         }
   521         // cycle detected and no one is catching the error.
   522         reportError(new ValidationEventImpl(
   523             ValidationEvent.ERROR,
   524             Messages.CYCLE_IN_MARSHALLER.format(cycleDetectionStack.getCycleString()),
   525             getCurrentLocation(fieldName),
   526             null));
   527         return null;
   528     }
   530     /**
   531      * The equivalent of:
   532      *
   533      * <pre>
   534      * childAsURIs(child, fieldName);
   535      * endNamespaceDecls();
   536      * childAsAttributes(child, fieldName);
   537      * endAttributes();
   538      * childAsBody(child, fieldName);
   539      * </pre>
   540      *
   541      * This produces the given child object as the sole content of
   542      * an element.
   543      * Used to reduce the code size in the generated marshaller.
   544      */
   545     public final void childAsSoleContent( Object child, String fieldName) throws SAXException, IOException, XMLStreamException {
   546         if(child==null) {
   547             handleMissingObjectError(fieldName);
   548         } else {
   549             child = pushObject(child,fieldName);
   550             if(child==null) {
   551                 // error recovery
   552                 endNamespaceDecls(null);
   553                 endAttributes();
   554                 cycleDetectionStack.pop();
   555             }
   557             JaxBeanInfo beanInfo;
   558             try {
   559                 beanInfo = grammar.getBeanInfo(child,true);
   560             } catch (JAXBException e) {
   561                 reportError(fieldName,e);
   562                 // recover by ignore
   563                 endNamespaceDecls(null);
   564                 endAttributes();
   565                 cycleDetectionStack.pop();
   566                 return;
   567             }
   569             final boolean lookForLifecycleMethods = beanInfo.lookForLifecycleMethods();
   570             if (lookForLifecycleMethods) {
   571                 fireBeforeMarshalEvents(beanInfo, child);
   572             }
   574             beanInfo.serializeURIs(child,this);
   575             endNamespaceDecls(child);
   576             beanInfo.serializeAttributes(child,this);
   577             endAttributes();
   578             beanInfo.serializeBody(child,this);
   580             if (lookForLifecycleMethods) {
   581                 fireAfterMarshalEvents(beanInfo, child);
   582             }
   584             cycleDetectionStack.pop();
   585         }
   586     }
   589     // the version of childAsXXX where it produces @xsi:type if the expected type name
   590     // and the actual type name differs.
   592     /**
   593      * This method is called when a type child object is found.
   594      *
   595      * <p>
   596      * This method produces events of the following form:
   597      * <pre>
   598      * NSDECL* "endNamespaceDecls" ATTRIBUTE* "endAttributes" BODY
   599      * </pre>
   600      * optionally including @xsi:type if necessary.
   601      *
   602      * @param child
   603      *      Object to be marshalled. The {@link JaxBeanInfo} for
   604      *      this object must return a type name.
   605      * @param expected
   606      *      Expected type of the object.
   607      * @param fieldName
   608      *      property name of the parent objeect from which 'o' comes.
   609      *      Used as a part of the error message in case anything goes wrong
   610      *      with 'o'.
   611      */
   612     public final void childAsXsiType( Object child, String fieldName, JaxBeanInfo expected, boolean nillable) throws SAXException, IOException, XMLStreamException {
   613         if(child==null) {
   614             handleMissingObjectError(fieldName);
   615         } else {
   616             child = pushObject(child,fieldName);
   617             if(child==null) { // error recovery
   618                 endNamespaceDecls(null);
   619                 endAttributes();
   620                 return;
   621             }
   623             boolean asExpected = child.getClass()==expected.jaxbType;
   624             JaxBeanInfo actual = expected;
   625             QName actualTypeName = null;
   627             if((asExpected) && (actual.lookForLifecycleMethods())) {
   628                 fireBeforeMarshalEvents(actual, child);
   629             }
   631             if(!asExpected) {
   632                 try {
   633                     actual = grammar.getBeanInfo(child,true);
   634                     if (actual.lookForLifecycleMethods()) {
   635                         fireBeforeMarshalEvents(actual, child);
   636                     }
   637                 } catch (JAXBException e) {
   638                     reportError(fieldName,e);
   639                     endNamespaceDecls(null);
   640                     endAttributes();
   641                     return; // recover by ignore
   642                 }
   643                 if(actual==expected)
   644                     asExpected = true;
   645                 else {
   646                     actualTypeName = actual.getTypeName(child);
   647                     if(actualTypeName==null) {
   648                         reportError(new ValidationEventImpl(
   649                                 ValidationEvent.ERROR,
   650                                 Messages.SUBSTITUTED_BY_ANONYMOUS_TYPE.format(
   651                                     expected.jaxbType.getName(),
   652                                     child.getClass().getName(),
   653                                     actual.jaxbType.getName()),
   654                                 getCurrentLocation(fieldName)));
   655                         // recover by not printing @xsi:type
   656                     } else {
   657                         getNamespaceContext().declareNamespace(WellKnownNamespace.XML_SCHEMA_INSTANCE,"xsi",true);
   658                         getNamespaceContext().declareNamespace(actualTypeName.getNamespaceURI(),null,false);
   659                     }
   660                 }
   661             }
   662             actual.serializeURIs(child,this);
   664             if (nillable) {
   665                 getNamespaceContext().declareNamespace(WellKnownNamespace.XML_SCHEMA_INSTANCE,"xsi",true);
   666             }
   668             endNamespaceDecls(child);
   669             if(!asExpected) {
   670                 attribute(WellKnownNamespace.XML_SCHEMA_INSTANCE,"type",
   671                     DatatypeConverter.printQName(actualTypeName,getNamespaceContext()));
   672             }
   674             actual.serializeAttributes(child,this);
   675             boolean nilDefined = actual.isNilIncluded();
   676             if ((nillable) && (!nilDefined)) {
   677                 attribute(WellKnownNamespace.XML_SCHEMA_INSTANCE,"nil","true");
   678             }
   680             endAttributes();
   681             actual.serializeBody(child,this);
   683             if (actual.lookForLifecycleMethods()) {
   684                 fireAfterMarshalEvents(actual, child);
   685             }
   687             cycleDetectionStack.pop();
   688         }
   689     }
   691     /**
   692      * Invoke the afterMarshal api on the external listener (if it exists) and on the bean embedded
   693      * afterMarshal api(if it exists).
   694      *
   695      * This method is called only after the callee has determined that beanInfo.lookForLifecycleMethods == true.
   696      *
   697      * @param beanInfo
   698      * @param currentTarget
   699      */
   700     private void fireAfterMarshalEvents(final JaxBeanInfo beanInfo, Object currentTarget) {
   701         // first invoke bean embedded listener
   702         if (beanInfo.hasAfterMarshalMethod()) {
   703             Method m = beanInfo.getLifecycleMethods().afterMarshal;
   704             fireMarshalEvent(currentTarget, m);
   705         }
   707         // then invoke external listener before bean embedded listener
   708         Marshaller.Listener externalListener = marshaller.getListener();
   709         if (externalListener != null) {
   710             externalListener.afterMarshal(currentTarget);
   711         }
   713     }
   715     /**
   716      * Invoke the beforeMarshal api on the external listener (if it exists) and on the bean embedded
   717      * beforeMarshal api(if it exists).
   718      *
   719      * This method is called only after the callee has determined that beanInfo.lookForLifecycleMethods == true.
   720      *
   721      * @param beanInfo
   722      * @param currentTarget
   723      */
   724     private void fireBeforeMarshalEvents(final JaxBeanInfo beanInfo, Object currentTarget) {
   725         // first invoke bean embedded listener
   726         if (beanInfo.hasBeforeMarshalMethod()) {
   727             Method m = beanInfo.getLifecycleMethods().beforeMarshal;
   728             fireMarshalEvent(currentTarget, m);
   729         }
   731         // then invoke external listener
   732         Marshaller.Listener externalListener = marshaller.getListener();
   733         if (externalListener != null) {
   734             externalListener.beforeMarshal(currentTarget);
   735         }
   736     }
   738     private void fireMarshalEvent(Object target, Method m) {
   739         try {
   740             m.invoke(target, marshaller);
   741         } catch (Exception e) {
   742             // this really only happens if there is a bug in the ri
   743             throw new IllegalStateException(e);
   744         }
   745     }
   747     public void attWildcardAsURIs(Map<QName,String> attributes, String fieldName) {
   748         if(attributes==null)    return;
   749         for( Map.Entry<QName,String> e : attributes.entrySet() ) {
   750             QName n = e.getKey();
   751             String nsUri = n.getNamespaceURI();
   752             if(nsUri.length()>0) {
   753                 String p = n.getPrefix();
   754                 if(p.length()==0)   p=null;
   755                 nsContext.declareNsUri(nsUri, p, true);
   756             }
   757         }
   758     }
   760     public void attWildcardAsAttributes(Map<QName,String> attributes, String fieldName) throws SAXException {
   761         if(attributes==null)    return;
   762         for( Map.Entry<QName,String> e : attributes.entrySet() ) {
   763             QName n = e.getKey();
   764             attribute(n.getNamespaceURI(),n.getLocalPart(),e.getValue());
   765         }
   766     }
   768     /**
   769      * Short for the following call sequence:
   770      *
   771      * <pre>
   772          getNamespaceContext().declareNamespace(WellKnownNamespace.XML_SCHEMA_INSTANCE,"xsi",true);
   773          endNamespaceDecls();
   774          attribute(WellKnownNamespace.XML_SCHEMA_INSTANCE,"nil","true");
   775          endAttributes();
   776      * </pre>
   777      */
   778     public final void writeXsiNilTrue() throws SAXException, IOException, XMLStreamException {
   779         getNamespaceContext().declareNamespace(WellKnownNamespace.XML_SCHEMA_INSTANCE,"xsi",true);
   780         endNamespaceDecls(null);
   781         attribute(WellKnownNamespace.XML_SCHEMA_INSTANCE,"nil","true");
   782         endAttributes();
   783     }
   785     public <E> void writeDom(E element, DomHandler<E, ?> domHandler, Object parentBean, String fieldName) throws SAXException {
   786         Source source = domHandler.marshal(element,this);
   787         if(contentHandlerAdapter==null)
   788             contentHandlerAdapter = new ContentHandlerAdaptor(this);
   789         try {
   790             getIdentityTransformer().transform(source,new SAXResult(contentHandlerAdapter));
   791         } catch (TransformerException e) {
   792             reportError(fieldName,e);
   793         }
   794     }
   796     public Transformer getIdentityTransformer() {
   797         if (identityTransformer==null)
   798             identityTransformer = JAXBContextImpl.createTransformer(grammar.disableSecurityProcessing);
   799         return identityTransformer;
   800     }
   802     public void setPrefixMapper(NamespacePrefixMapper prefixMapper) {
   803         nsContext.setPrefixMapper(prefixMapper);
   804     }
   806     /**
   807      * Reset this object to write to the specified output.
   808      *
   809      * @param schemaLocation
   810      *      if non-null, this value is printed on the root element as xsi:schemaLocation
   811      * @param noNsSchemaLocation
   812      *      Similar to 'schemaLocation' but this one works for xsi:noNamespaceSchemaLocation
   813      */
   814     public void startDocument(XmlOutput out,boolean fragment,String schemaLocation,String noNsSchemaLocation) throws IOException, SAXException, XMLStreamException {
   815         pushCoordinator();
   816         nsContext.reset();
   817         nse = nsContext.getCurrent();
   818         if(attachmentMarshaller!=null && attachmentMarshaller.isXOPPackage())
   819             out = new MTOMXmlOutput(out);
   820         this.out = out;
   821         objectsWithId.clear();
   822         idReferencedObjects.clear();
   823         textHasAlreadyPrinted = false;
   824         seenRoot = false;
   825         this.schemaLocation = schemaLocation;
   826         this.noNsSchemaLocation = noNsSchemaLocation;
   827         this.fragment = fragment;
   828         this.inlineBinaryFlag = false;
   829         this.expectedMimeType = null;
   830         cycleDetectionStack.reset();
   832         out.startDocument(this,fragment,knownUri2prefixIndexMap,nsContext);
   833     }
   835     public void endDocument() throws IOException, SAXException, XMLStreamException {
   836         out.endDocument(fragment);
   837     }
   839     public void close() {
   840         out = null;
   841         clearCurrentProperty();
   842         popCoordinator();
   843     }
   845     /**
   846      * This method can be called after {@link #startDocument} is called
   847      * but before the marshalling begins, to set the currently in-scope namespace
   848      * bindings.
   849      *
   850      * <p>
   851      * This method is useful to avoid redundant namespace declarations when
   852      * the marshalling is producing a sub-document.
   853      */
   854     public void addInscopeBinding(String nsUri,String prefix) {
   855         nsContext.put(nsUri,prefix);
   856     }
   858     /**
   859      * Gets the MIME type with which the binary content shall be printed.
   860      *
   861      * <p>
   862      * This method shall be used from those {@link RuntimeBuiltinLeafInfo} that are
   863      * bound to base64Binary.
   864      *
   865      * @see JAXBContextImpl#getXMIMEContentType(Object)
   866      */
   867     public String getXMIMEContentType() {
   868         // xmime:contentType takes precedence
   869         String v = grammar.getXMIMEContentType(cycleDetectionStack.peek());
   870         if(v!=null)     return v;
   872         // then look for the current in-scope @XmlMimeType
   873         if(expectedMimeType!=null)
   874             return expectedMimeType.toString();
   876         return null;
   877     }
   879     private void startElement() {
   880         nse = nse.push();
   882         if( !seenRoot ) {
   884             if (grammar.getXmlNsSet() != null) {
   885                 for(XmlNs xmlNs : grammar.getXmlNsSet())
   886                     nsContext.declareNsUri(
   887                         xmlNs.namespaceURI(),
   888                         xmlNs.prefix() == null ? "" : xmlNs.prefix(),
   889                         xmlNs.prefix() != null);
   890             }
   892             // seenRoot set to true in endAttributes
   893             // first declare all known URIs
   894             String[] knownUris = nameList.namespaceURIs;
   895             for( int i=0; i<knownUris.length; i++ )
   896                 knownUri2prefixIndexMap[i] = nsContext.declareNsUri(knownUris[i], null, nameList.nsUriCannotBeDefaulted[i]);
   898             // then declare user-specified namespace URIs.
   899             // work defensively. we are calling an user-defined method.
   900             String[] uris = nsContext.getPrefixMapper().getPreDeclaredNamespaceUris();
   901             if( uris!=null ) {
   902                 for (String uri : uris) {
   903                     if (uri != null)
   904                         nsContext.declareNsUri(uri, null, false);
   905                 }
   906             }
   907             String[] pairs = nsContext.getPrefixMapper().getPreDeclaredNamespaceUris2();
   908             if( pairs!=null ) {
   909                 for( int i=0; i<pairs.length; i+=2 ) {
   910                     String prefix = pairs[i];
   911                     String nsUri = pairs[i+1];
   912                     if(prefix!=null && nsUri!=null)
   913                         // in this case, we don't want the redundant binding consolidation
   914                         // to happen (such as declaring the same namespace URI twice with
   915                         // different prefixes.) Hence we call the put method directly.
   916                         nsContext.put(nsUri,prefix);
   917                 }
   918             }
   920             if(schemaLocation!=null || noNsSchemaLocation!=null) {
   921                 nsContext.declareNsUri(WellKnownNamespace.XML_SCHEMA_INSTANCE,"xsi",true);
   922             }
   923         }
   925         nsContext.collectionMode = true;
   926         textHasAlreadyPrinted = false;
   927     }
   929     private MimeType expectedMimeType;
   931     /**
   932      * This method is used by {@link MimeTypedTransducer} to set the expected MIME type
   933      * for the encapsulated {@link Transducer}.
   934      */
   935     public MimeType setExpectedMimeType(MimeType expectedMimeType) {
   936         MimeType old = this.expectedMimeType;
   937         this.expectedMimeType = expectedMimeType;
   938         return old;
   939     }
   941     /**
   942      * True to force inlining.
   943      */
   944     private boolean inlineBinaryFlag;
   946     public boolean setInlineBinaryFlag(boolean value) {
   947         boolean old = inlineBinaryFlag;
   948         this.inlineBinaryFlag = value;
   949         return old;
   950     }
   952     public boolean getInlineBinaryFlag() {
   953         return inlineBinaryFlag;
   954     }
   956     /**
   957      * Field used to support an {@link XmlSchemaType} annotation.
   958      *
   959      * <p>
   960      * When we are marshalling a property with an effective {@link XmlSchemaType},
   961      * this field is set to hold the QName of that type. The {@link Transducer} that
   962      * actually converts a Java object into XML can look this property to decide
   963      * how to marshal the value.
   964      */
   965     private QName schemaType;
   967     public QName setSchemaType(QName st) {
   968         QName old = schemaType;
   969         schemaType = st;
   970         return old;
   971     }
   973     public QName getSchemaType() {
   974         return schemaType;
   975     }
   977     public void setObjectIdentityCycleDetection(boolean val) {
   978         cycleDetectionStack.setUseIdentity(val);
   979     }
   980     public boolean getObjectIdentityCycleDetection() {
   981         return cycleDetectionStack.getUseIdentity();
   982     }
   984     void reconcileID() throws SAXException {
   985         // find objects that were not a part of the object graph
   986         idReferencedObjects.removeAll(objectsWithId);
   988         for( Object idObj : idReferencedObjects ) {
   989             try {
   990                 String id = getIdFromObject(idObj);
   991                 reportError( new NotIdentifiableEventImpl(
   992                     ValidationEvent.ERROR,
   993                     Messages.DANGLING_IDREF.format(id),
   994                     new ValidationEventLocatorImpl(idObj) ) );
   995             } catch (JAXBException e) {
   996                 // this error should have been reported already. just ignore here.
   997             }
   998         }
  1000         // clear the garbage
  1001         idReferencedObjects.clear();
  1002         objectsWithId.clear();
  1005     public boolean handleError(Exception e) {
  1006         return handleError(e,cycleDetectionStack.peek(),null);
  1009     public boolean handleError(Exception e,Object source,String fieldName) {
  1010         return handleEvent(
  1011             new ValidationEventImpl(
  1012                 ValidationEvent.ERROR,
  1013                 e.getMessage(),
  1014                 new ValidationEventLocatorExImpl(source,fieldName),
  1015                     e));
  1018     public boolean handleEvent(ValidationEvent event) {
  1019         try {
  1020             return marshaller.getEventHandler().handleEvent(event);
  1021         } catch (JAXBException e) {
  1022             // impossible
  1023             throw new Error(e);
  1027     private void reportMissingObjectError(String fieldName) throws SAXException {
  1028         reportError(new ValidationEventImpl(
  1029             ValidationEvent.ERROR,
  1030             Messages.MISSING_OBJECT.format(fieldName),
  1031                 getCurrentLocation(fieldName),
  1032             new NullPointerException() ));
  1035     /**
  1036      * Called when a referenced object doesn't have an ID.
  1037      */
  1038     public void errorMissingId(Object obj) throws SAXException {
  1039         reportError( new ValidationEventImpl(
  1040             ValidationEvent.ERROR,
  1041             Messages.MISSING_ID.format(obj),
  1042             new ValidationEventLocatorImpl(obj)) );
  1045     public ValidationEventLocator getCurrentLocation(String fieldName) {
  1046         return new ValidationEventLocatorExImpl(cycleDetectionStack.peek(),fieldName);
  1049     protected ValidationEventLocator getLocation() {
  1050         return getCurrentLocation(null);
  1053     /**
  1054      * May return null when the property hasn't been set.
  1055      * Introduced based on Jersey requirements.
  1056      */
  1057     public Property getCurrentProperty() {
  1058         return currentProperty.get();
  1061     /**
  1062      * Takes care of cleaning the currentProperty. Must be called from the same thread that created the XMLSerializer.
  1063      */
  1064     public void clearCurrentProperty() {
  1065         if (currentProperty != null) {
  1066             currentProperty.remove();
  1070     /**
  1071      * When called from within the realm of the marshaller, this method
  1072      * returns the current {@link XMLSerializer} in charge.
  1073      */
  1074     public static XMLSerializer getInstance() {
  1075         return (XMLSerializer)Coordinator._getInstance();

mercurial