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

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

author
aoqi
date
Thu, 31 Aug 2017 15:18:52 +0800
changeset 637
9c07ef4934dd
parent 397
b99d7e355d4b
parent 0
373ffda63c9a
permissions
-rw-r--r--

merge

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

mercurial