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

Fri, 04 Oct 2013 16:21:34 +0100

author
mkos
date
Fri, 04 Oct 2013 16:21:34 +0100
changeset 408
b0610cd08440
parent 397
b99d7e355d4b
child 637
9c07ef4934dd
permissions
-rw-r--r--

8025054: Update JAX-WS RI integration to 2.2.9-b130926.1035
Reviewed-by: chegar

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

mercurial