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

Fri, 23 Aug 2013 09:57:21 +0100

author
mkos
date
Fri, 23 Aug 2013 09:57:21 +0100
changeset 397
b99d7e355d4b
parent 286
f50545b5e2f1
child 637
9c07ef4934dd
child 650
121e938cb9c3
permissions
-rw-r--r--

8022885: Update JAX-WS RI integration to 2.2.9-b14140
8013016: Rebase 8009009 against the latest jdk8/jaxws
Reviewed-by: alanb, chegar

ohair@286 1 /*
mkos@397 2 * Copyright (c) 1997, 2013, 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.unmarshaller;
ohair@286 27
ohair@286 28 import java.lang.reflect.InvocationTargetException;
ohair@286 29 import java.lang.reflect.Method;
ohair@286 30 import java.util.ArrayList;
ohair@286 31 import java.util.Collection;
ohair@286 32 import java.util.Collections;
ohair@286 33 import java.util.HashMap;
ohair@286 34 import java.util.Iterator;
ohair@286 35 import java.util.List;
ohair@286 36 import java.util.Map;
ohair@286 37 import java.util.concurrent.Callable;
mkos@397 38 import java.util.logging.Level;
mkos@397 39 import java.util.logging.Logger;
ohair@286 40
ohair@286 41 import javax.xml.XMLConstants;
ohair@286 42 import javax.xml.bind.JAXBElement;
ohair@286 43 import javax.xml.bind.UnmarshalException;
ohair@286 44 import javax.xml.bind.Unmarshaller;
ohair@286 45 import javax.xml.bind.ValidationEvent;
ohair@286 46 import javax.xml.bind.ValidationEventHandler;
ohair@286 47 import javax.xml.bind.ValidationEventLocator;
ohair@286 48 import javax.xml.bind.helpers.ValidationEventImpl;
ohair@286 49 import javax.xml.namespace.NamespaceContext;
ohair@286 50 import javax.xml.namespace.QName;
ohair@286 51
ohair@286 52 import com.sun.istack.internal.NotNull;
ohair@286 53 import com.sun.istack.internal.Nullable;
ohair@286 54 import com.sun.istack.internal.SAXParseException2;
ohair@286 55 import com.sun.xml.internal.bind.IDResolver;
mkos@397 56 import com.sun.xml.internal.bind.Util;
ohair@286 57 import com.sun.xml.internal.bind.api.AccessorException;
ohair@286 58 import com.sun.xml.internal.bind.api.ClassResolver;
ohair@286 59 import com.sun.xml.internal.bind.unmarshaller.InfosetScanner;
ohair@286 60 import com.sun.xml.internal.bind.v2.ClassFactory;
ohair@286 61 import com.sun.xml.internal.bind.v2.runtime.AssociationMap;
ohair@286 62 import com.sun.xml.internal.bind.v2.runtime.Coordinator;
ohair@286 63 import com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl;
ohair@286 64 import com.sun.xml.internal.bind.v2.runtime.JaxBeanInfo;
mkos@397 65 import java.util.logging.Level;
mkos@397 66 import java.util.logging.Logger;
ohair@286 67
ohair@286 68 import org.xml.sax.ErrorHandler;
ohair@286 69 import org.xml.sax.SAXException;
ohair@286 70 import org.xml.sax.helpers.LocatorImpl;
ohair@286 71
ohair@286 72 /**
ohair@286 73 * Center of the unmarshalling.
ohair@286 74 *
ohair@286 75 * <p>
ohair@286 76 * This object is responsible for coordinating {@link Loader}s to
ohair@286 77 * perform the whole unmarshalling.
ohair@286 78 *
ohair@286 79 * @author Kohsuke Kawaguchi
ohair@286 80 */
ohair@286 81 public final class UnmarshallingContext extends Coordinator
ohair@286 82 implements NamespaceContext, ValidationEventHandler, ErrorHandler, XmlVisitor, XmlVisitor.TextPredictor {
ohair@286 83
mkos@397 84 private static final Logger logger = Logger.getLogger(UnmarshallingContext.class.getName());
mkos@397 85
ohair@286 86 /**
ohair@286 87 * Root state.
ohair@286 88 */
ohair@286 89 private final State root;
ohair@286 90
ohair@286 91 /**
ohair@286 92 * The currently active state.
ohair@286 93 */
ohair@286 94 private State current;
ohair@286 95
ohair@286 96 private static final LocatorEx DUMMY_INSTANCE;
ohair@286 97
ohair@286 98 static {
ohair@286 99 LocatorImpl loc = new LocatorImpl();
ohair@286 100 loc.setPublicId(null);
ohair@286 101 loc.setSystemId(null);
ohair@286 102 loc.setLineNumber(-1);
ohair@286 103 loc.setColumnNumber(-1);
ohair@286 104 DUMMY_INSTANCE = new LocatorExWrapper(loc);
ohair@286 105 }
ohair@286 106
ohair@286 107 private @NotNull LocatorEx locator = DUMMY_INSTANCE;
ohair@286 108
ohair@286 109 /** Root object that is being unmarshalled. */
ohair@286 110 private Object result;
ohair@286 111
ohair@286 112 /**
ohair@286 113 * If non-null, this unmarshaller will unmarshal {@code JAXBElement<EXPECTEDTYPE>}
ohair@286 114 * regardless of the tag name, as opposed to deciding the root object by using
ohair@286 115 * the tag name.
ohair@286 116 *
ohair@286 117 * The property has a package-level access, because we cannot copy this value
ohair@286 118 * to {@link UnmarshallingContext} when it is created. The property
ohair@286 119 * on {@link Unmarshaller} could be changed after the handler is created.
ohair@286 120 */
ohair@286 121 private JaxBeanInfo expectedType;
ohair@286 122
ohair@286 123 /**
ohair@286 124 * Handles ID/IDREF.
ohair@286 125 */
ohair@286 126 private IDResolver idResolver;
ohair@286 127
ohair@286 128 /**
ohair@286 129 * This flag is set to true at the startDocument event
ohair@286 130 * and false at the endDocument event.
ohair@286 131 *
ohair@286 132 * Until the first document is unmarshalled, we don't
ohair@286 133 * want to return an object. So this variable is initialized
ohair@286 134 * to true.
ohair@286 135 */
ohair@286 136 private boolean isUnmarshalInProgress = true;
ohair@286 137 private boolean aborted = false;
ohair@286 138
ohair@286 139 public final UnmarshallerImpl parent;
ohair@286 140
ohair@286 141 /**
ohair@286 142 * If the unmarshaller is doing associative unmarshalling,
ohair@286 143 * this field is initialized to non-null.
ohair@286 144 */
ohair@286 145 private final AssociationMap assoc;
ohair@286 146
ohair@286 147 /**
ohair@286 148 * Indicates whether we are doing in-place unmarshalling
ohair@286 149 * or not.
ohair@286 150 *
ohair@286 151 * <p>
ohair@286 152 * This flag is unused when {@link #assoc}==null.
ohair@286 153 * If it's non-null, then <tt>true</tt> indicates
ohair@286 154 * that we are doing in-place associative unmarshalling.
ohair@286 155 * If <tt>false</tt>, then we are doing associative unmarshalling
ohair@286 156 * without object reuse.
ohair@286 157 */
ohair@286 158 private boolean isInplaceMode;
ohair@286 159
ohair@286 160 /**
ohair@286 161 * This object is consulted to get the element object for
ohair@286 162 * the current element event.
ohair@286 163 *
ohair@286 164 * This is used when we are building an association map.
ohair@286 165 */
ohair@286 166 private InfosetScanner scanner;
ohair@286 167
ohair@286 168 private Object currentElement;
ohair@286 169
ohair@286 170 /**
ohair@286 171 * @see XmlVisitor#startDocument(LocatorEx, NamespaceContext)
ohair@286 172 */
ohair@286 173 private NamespaceContext environmentNamespaceContext;
ohair@286 174
ohair@286 175 /**
ohair@286 176 * Used to discover additional classes when we hit unknown elements/types.
ohair@286 177 */
ohair@286 178 public @Nullable ClassResolver classResolver;
ohair@286 179
ohair@286 180 /**
ohair@286 181 * User-supplied {@link ClassLoader} for converting name to {@link Class}.
ohair@286 182 * For backward compatibility, when null, use thread context classloader.
ohair@286 183 */
ohair@286 184 public @Nullable ClassLoader classLoader;
ohair@286 185
ohair@286 186 /**
mkos@397 187 * The variable introduced to avoid reporting n^10 similar errors.
mkos@397 188 * After error is reported counter is decremented. When it became 0 - errors should not be reported any more.
mkos@397 189 *
mkos@397 190 * volatile is required to ensure that concurrent threads will see changed value
mkos@397 191 */
mkos@397 192 private static volatile int errorsCounter = 10;
mkos@397 193
mkos@397 194 /**
ohair@286 195 * State information for each element.
ohair@286 196 */
ohair@286 197 public final class State {
ohair@286 198 /**
ohair@286 199 * Loader that owns this element.
ohair@286 200 */
ohair@286 201 public Loader loader;
ohair@286 202 /**
ohair@286 203 * Once {@link #loader} is completed, this receiver
ohair@286 204 * receives the result.
ohair@286 205 */
ohair@286 206 public Receiver receiver;
ohair@286 207
ohair@286 208 public Intercepter intercepter;
ohair@286 209
ohair@286 210
ohair@286 211 /**
ohair@286 212 * Object being unmarshalled by this {@link #loader}.
ohair@286 213 */
ohair@286 214 public Object target;
ohair@286 215
ohair@286 216 /**
ohair@286 217 * Hack for making JAXBElement unmarshalling work.
ohair@286 218 *
ohair@286 219 * <p>
ohair@286 220 * While the unmarshalling is in progress, the {@link #target} field stores the object being unmarshalled.
ohair@286 221 * This makes it convenient to keep track of the unmarshalling activity in context of XML infoset, but
ohair@286 222 * since there's only one {@link State} per element, this mechanism only works when there's one object
ohair@286 223 * per element, which breaks down when we have {@link JAXBElement}, since the presence of JAXBElement
ohair@286 224 * requires that we have two objects unmarshalled (a JAXBElement X and a value object Y bound to an XML type.)
ohair@286 225 *
ohair@286 226 * <p>
ohair@286 227 * So to make room for storing both, this {@link #backup} field is used. When we create X instance
ohair@286 228 * in the above example, we set that to {@code state.prev.target} and displace its old value to
ohair@286 229 * {@code state.prev.backup} (where Y goes to {@code state.target}.) Upon the completion of the unmarshalling
ohair@286 230 * of Y, we revert this.
ohair@286 231 *
ohair@286 232 * <p>
ohair@286 233 * While this attributes X incorrectly to its parent element, this preserves the parent/child
ohair@286 234 * relationship between unmarshalled objects and {@link State} parent/child relationship, and
ohair@286 235 * it thereby makes {@link Receiver} mechanism simpler.
ohair@286 236 *
ohair@286 237 * <p>
ohair@286 238 * Yes, I know this is a hack, and no, I'm not proud of it.
ohair@286 239 *
ohair@286 240 * @see ElementBeanInfoImpl.IntercepterLoader#startElement(State, TagName)
ohair@286 241 * @see ElementBeanInfoImpl.IntercepterLoader#intercept(State, Object)
ohair@286 242 */
ohair@286 243 public Object backup;
ohair@286 244
ohair@286 245 /**
ohair@286 246 * Number of {@link UnmarshallingContext#nsBind}s declared thus far.
ohair@286 247 * (The value of {@link UnmarshallingContext#nsLen} when this state is pushed.
ohair@286 248 */
ohair@286 249 private int numNsDecl;
ohair@286 250
ohair@286 251 /**
ohair@286 252 * If this element has an element default value.
ohair@286 253 *
ohair@286 254 * This should be set by either a parent {@link Loader} when
ohair@286 255 * {@link Loader#childElement(State, TagName)} is called
ohair@286 256 * or by a child {@link Loader} when
ohair@286 257 * {@link Loader#startElement(State, TagName)} is called.
ohair@286 258 */
ohair@286 259 public String elementDefaultValue;
ohair@286 260
ohair@286 261 /**
ohair@286 262 * {@link State} for the parent element
ohair@286 263 *
ohair@286 264 * {@link State} objects form a doubly linked list.
ohair@286 265 */
ohair@286 266 public State prev;
ohair@286 267 private State next;
ohair@286 268
ohair@286 269 public boolean nil = false;
ohair@286 270
ohair@286 271 /**
ohair@286 272 * Gets the context.
ohair@286 273 */
ohair@286 274 public UnmarshallingContext getContext() {
ohair@286 275 return UnmarshallingContext.this;
ohair@286 276 }
ohair@286 277
mkos@397 278 @SuppressWarnings("LeakingThisInConstructor")
ohair@286 279 private State(State prev) {
ohair@286 280 this.prev = prev;
mkos@397 281 if (prev!=null) {
ohair@286 282 prev.next = this;
mkos@397 283 }
ohair@286 284 }
ohair@286 285
ohair@286 286 private void push() {
mkos@397 287 if (logger.isLoggable(Level.FINEST)) {
mkos@397 288 logger.log(Level.FINEST, "State.push");
mkos@397 289 }
mkos@397 290 if (next==null) {
mkos@397 291 assert current == this;
ohair@286 292 allocateMoreStates();
mkos@397 293 }
mkos@397 294 nil = false;
ohair@286 295 State n = next;
ohair@286 296 n.numNsDecl = nsLen;
ohair@286 297 current = n;
ohair@286 298 }
ohair@286 299
ohair@286 300 private void pop() {
mkos@397 301 if (logger.isLoggable(Level.FINEST)) {
mkos@397 302 logger.log(Level.FINEST, "State.pop");
mkos@397 303 }
ohair@286 304 assert prev!=null;
ohair@286 305 loader = null;
ohair@286 306 nil = false;
ohair@286 307 receiver = null;
ohair@286 308 intercepter = null;
ohair@286 309 elementDefaultValue = null;
ohair@286 310 target = null;
ohair@286 311 current = prev;
ohair@286 312 }
ohair@286 313 }
ohair@286 314
ohair@286 315 /**
ohair@286 316 * Stub to the user-specified factory method.
ohair@286 317 */
ohair@286 318 private static class Factory {
ohair@286 319 private final Object factorInstance;
ohair@286 320 private final Method method;
ohair@286 321
ohair@286 322 public Factory(Object factorInstance, Method method) {
ohair@286 323 this.factorInstance = factorInstance;
ohair@286 324 this.method = method;
ohair@286 325 }
ohair@286 326
ohair@286 327 public Object createInstance() throws SAXException {
ohair@286 328 try {
ohair@286 329 return method.invoke(factorInstance);
ohair@286 330 } catch (IllegalAccessException e) {
ohair@286 331 getInstance().handleError(e,false);
ohair@286 332 } catch (InvocationTargetException e) {
ohair@286 333 getInstance().handleError(e,false);
ohair@286 334 }
ohair@286 335 return null; // can never be executed
ohair@286 336 }
ohair@286 337 }
ohair@286 338
ohair@286 339
ohair@286 340 /**
ohair@286 341 * Creates a new unmarshaller.
ohair@286 342 *
ohair@286 343 * @param assoc
ohair@286 344 * Must be both non-null when the unmarshaller does the
ohair@286 345 * in-place unmarshalling. Otherwise must be both null.
ohair@286 346 */
ohair@286 347 public UnmarshallingContext( UnmarshallerImpl _parent, AssociationMap assoc) {
ohair@286 348 this.parent = _parent;
ohair@286 349 this.assoc = assoc;
ohair@286 350 this.root = this.current = new State(null);
ohair@286 351 allocateMoreStates();
ohair@286 352 }
ohair@286 353
ohair@286 354 public void reset(InfosetScanner scanner,boolean isInplaceMode, JaxBeanInfo expectedType, IDResolver idResolver) {
ohair@286 355 this.scanner = scanner;
ohair@286 356 this.isInplaceMode = isInplaceMode;
ohair@286 357 this.expectedType = expectedType;
ohair@286 358 this.idResolver = idResolver;
ohair@286 359 }
ohair@286 360
ohair@286 361 public JAXBContextImpl getJAXBContext() {
ohair@286 362 return parent.context;
ohair@286 363 }
ohair@286 364
ohair@286 365 public State getCurrentState() {
ohair@286 366 return current;
ohair@286 367 }
ohair@286 368
ohair@286 369 /**
ohair@286 370 * On top of {@link JAXBContextImpl#selectRootLoader(State, TagName)},
ohair@286 371 * this method also consults {@link ClassResolver}.
ohair@286 372 *
ohair@286 373 * @throws SAXException
ohair@286 374 * if {@link ValidationEventHandler} reported a failure.
ohair@286 375 */
ohair@286 376 public Loader selectRootLoader(State state, TagName tag) throws SAXException {
ohair@286 377 try {
ohair@286 378 Loader l = getJAXBContext().selectRootLoader(state, tag);
ohair@286 379 if(l!=null) return l;
ohair@286 380
ohair@286 381 if(classResolver!=null) {
ohair@286 382 Class<?> clazz = classResolver.resolveElementName(tag.uri, tag.local);
ohair@286 383 if(clazz!=null) {
ohair@286 384 JAXBContextImpl enhanced = getJAXBContext().createAugmented(clazz);
ohair@286 385 JaxBeanInfo<?> bi = enhanced.getBeanInfo(clazz);
ohair@286 386 return bi.getLoader(enhanced,true);
ohair@286 387 }
ohair@286 388 }
ohair@286 389 } catch (RuntimeException e) {
ohair@286 390 throw e;
ohair@286 391 } catch (Exception e) {
ohair@286 392 handleError(e);
ohair@286 393 }
ohair@286 394
ohair@286 395 return null;
ohair@286 396 }
ohair@286 397
ohair@286 398 /**
ohair@286 399 * Allocates a few more {@link State}s.
ohair@286 400 *
ohair@286 401 * Allocating multiple {@link State}s at once allows those objects
ohair@286 402 * to be allocated near each other, which reduces the working set
ohair@286 403 * of CPU. It improves the chance the relevant data is in the cache.
ohair@286 404 */
ohair@286 405 private void allocateMoreStates() {
ohair@286 406 // this method should be used only when we run out of a state.
ohair@286 407 assert current.next==null;
ohair@286 408
ohair@286 409 State s = current;
mkos@397 410 for (int i=0; i<8; i++) {
ohair@286 411 s = new State(s);
mkos@397 412 }
ohair@286 413 }
ohair@286 414
ohair@286 415 public void clearStates() {
ohair@286 416 State last = current;
ohair@286 417 while (last.next != null) last = last.next;
ohair@286 418 while (last.prev != null) {
ohair@286 419 last.loader = null;
ohair@286 420 last.nil = false;
ohair@286 421 last.receiver = null;
ohair@286 422 last.intercepter = null;
ohair@286 423 last.elementDefaultValue = null;
ohair@286 424 last.target = null;
ohair@286 425 last = last.prev;
ohair@286 426 last.next.prev = null;
ohair@286 427 last.next = null;
ohair@286 428 }
ohair@286 429 current = last;
ohair@286 430 }
ohair@286 431
ohair@286 432 /**
ohair@286 433 * User-specified factory methods.
ohair@286 434 */
ohair@286 435 private final Map<Class,Factory> factories = new HashMap<Class, Factory>();
ohair@286 436
ohair@286 437 public void setFactories(Object factoryInstances) {
ohair@286 438 factories.clear();
ohair@286 439 if(factoryInstances==null) {
ohair@286 440 return;
ohair@286 441 }
ohair@286 442 if(factoryInstances instanceof Object[]) {
ohair@286 443 for( Object factory : (Object[])factoryInstances ) {
ohair@286 444 // look for all the public methods inlcuding derived ones
ohair@286 445 addFactory(factory);
ohair@286 446 }
ohair@286 447 } else {
ohair@286 448 addFactory(factoryInstances);
ohair@286 449 }
ohair@286 450 }
ohair@286 451
ohair@286 452 private void addFactory(Object factory) {
ohair@286 453 for( Method m : factory.getClass().getMethods() ) {
ohair@286 454 // look for methods whose signature is T createXXX()
ohair@286 455 if(!m.getName().startsWith("create"))
ohair@286 456 continue;
ohair@286 457 if(m.getParameterTypes().length>0)
ohair@286 458 continue;
ohair@286 459
ohair@286 460 Class type = m.getReturnType();
ohair@286 461
ohair@286 462 factories.put(type,new Factory(factory,m));
ohair@286 463 }
ohair@286 464 }
ohair@286 465
mkos@397 466 @Override
ohair@286 467 public void startDocument(LocatorEx locator, NamespaceContext nsContext) throws SAXException {
ohair@286 468 if(locator!=null)
ohair@286 469 this.locator = locator;
ohair@286 470 this.environmentNamespaceContext = nsContext;
ohair@286 471 // reset the object
ohair@286 472 result = null;
ohair@286 473 current = root;
ohair@286 474
ohair@286 475 patchersLen=0;
ohair@286 476 aborted = false;
ohair@286 477 isUnmarshalInProgress = true;
ohair@286 478 nsLen=0;
ohair@286 479
ohair@286 480 if(expectedType!=null)
ohair@286 481 root.loader = EXPECTED_TYPE_ROOT_LOADER;
ohair@286 482 else
ohair@286 483 root.loader = DEFAULT_ROOT_LOADER;
ohair@286 484
ohair@286 485 idResolver.startDocument(this);
ohair@286 486 }
ohair@286 487
mkos@397 488 @Override
ohair@286 489 public void startElement(TagName tagName) throws SAXException {
ohair@286 490 pushCoordinator();
ohair@286 491 try {
ohair@286 492 _startElement(tagName);
ohair@286 493 } finally {
ohair@286 494 popCoordinator();
ohair@286 495 }
ohair@286 496 }
ohair@286 497
ohair@286 498 private void _startElement(TagName tagName) throws SAXException {
ohair@286 499 // remember the current element if we are interested in it.
ohair@286 500 // because the inner peer might not be found while we consume
ohair@286 501 // the enter element token, we need to keep this information
ohair@286 502 // longer than this callback. That's why we assign it to a field.
ohair@286 503 if( assoc!=null )
ohair@286 504 currentElement = scanner.getCurrentElement();
ohair@286 505
ohair@286 506 Loader h = current.loader;
ohair@286 507 current.push();
ohair@286 508
ohair@286 509 // tell the parent about the new child
ohair@286 510 h.childElement(current,tagName);
ohair@286 511 assert current.loader!=null; // the childElement should register this
ohair@286 512 // and tell the new child that you are activated
ohair@286 513 current.loader.startElement(current,tagName);
ohair@286 514 }
ohair@286 515
mkos@397 516 @Override
ohair@286 517 public void text(CharSequence pcdata) throws SAXException {
ohair@286 518 State cur = current;
ohair@286 519 pushCoordinator();
ohair@286 520 try {
ohair@286 521 if(cur.elementDefaultValue!=null) {
ohair@286 522 if(pcdata.length()==0) {
ohair@286 523 // send the default value into the unmarshaller instead
ohair@286 524 pcdata = cur.elementDefaultValue;
ohair@286 525 }
ohair@286 526 }
ohair@286 527 cur.loader.text(cur,pcdata);
ohair@286 528 } finally {
ohair@286 529 popCoordinator();
ohair@286 530 }
ohair@286 531 }
ohair@286 532
mkos@397 533 @Override
ohair@286 534 public final void endElement(TagName tagName) throws SAXException {
ohair@286 535 pushCoordinator();
ohair@286 536 try {
ohair@286 537 State child = current;
ohair@286 538
ohair@286 539 // tell the child that your time is up
ohair@286 540 child.loader.leaveElement(child,tagName);
ohair@286 541
ohair@286 542 // child.pop will erase them so store them now
ohair@286 543 Object target = child.target;
ohair@286 544 Receiver recv = child.receiver;
ohair@286 545 Intercepter intercepter = child.intercepter;
ohair@286 546 child.pop();
ohair@286 547
ohair@286 548 // then let the parent know
ohair@286 549 if(intercepter!=null)
ohair@286 550 target = intercepter.intercept(current,target);
ohair@286 551 if(recv!=null)
ohair@286 552 recv.receive(current,target);
ohair@286 553 } finally {
ohair@286 554 popCoordinator();
ohair@286 555 }
ohair@286 556 }
ohair@286 557
mkos@397 558 @Override
ohair@286 559 public void endDocument() throws SAXException {
ohair@286 560 runPatchers();
ohair@286 561 idResolver.endDocument();
ohair@286 562
ohair@286 563 isUnmarshalInProgress = false;
ohair@286 564 currentElement = null;
ohair@286 565 locator = DUMMY_INSTANCE;
ohair@286 566 environmentNamespaceContext = null;
ohair@286 567
ohair@286 568 // at the successful completion, scope must be all closed
ohair@286 569 assert root==current;
ohair@286 570 }
ohair@286 571
ohair@286 572 /**
ohair@286 573 * You should be always calling this through {@link TextPredictor}.
ohair@286 574 */
ohair@286 575 @Deprecated
mkos@397 576 @Override
ohair@286 577 public boolean expectText() {
ohair@286 578 return current.loader.expectText;
ohair@286 579 }
ohair@286 580
ohair@286 581 /**
ohair@286 582 * You should be always getting {@link TextPredictor} from {@link XmlVisitor}.
ohair@286 583 */
ohair@286 584 @Deprecated
mkos@397 585 @Override
ohair@286 586 public TextPredictor getPredictor() {
ohair@286 587 return this;
ohair@286 588 }
ohair@286 589
mkos@397 590 @Override
ohair@286 591 public UnmarshallingContext getContext() {
ohair@286 592 return this;
ohair@286 593 }
ohair@286 594
ohair@286 595 /**
ohair@286 596 * Gets the result of the unmarshalling
ohair@286 597 */
ohair@286 598 public Object getResult() throws UnmarshalException {
ohair@286 599 if(isUnmarshalInProgress)
ohair@286 600 throw new IllegalStateException();
ohair@286 601
ohair@286 602 if(!aborted) return result;
ohair@286 603
ohair@286 604 // there was an error.
ohair@286 605 throw new UnmarshalException((String)null);
ohair@286 606 }
ohair@286 607
ohair@286 608 void clearResult() {
ohair@286 609 if (isUnmarshalInProgress) {
ohair@286 610 throw new IllegalStateException();
ohair@286 611 }
ohair@286 612 result = null;
ohair@286 613 }
ohair@286 614
ohair@286 615 /**
ohair@286 616 * Creates a new instance of the specified class.
ohair@286 617 * In the unmarshaller, we need to check the user-specified factory class.
ohair@286 618 */
ohair@286 619 public Object createInstance( Class<?> clazz ) throws SAXException {
ohair@286 620 if(!factories.isEmpty()) {
ohair@286 621 Factory factory = factories.get(clazz);
ohair@286 622 if(factory!=null)
ohair@286 623 return factory.createInstance();
ohair@286 624 }
ohair@286 625 return ClassFactory.create(clazz);
ohair@286 626 }
ohair@286 627
ohair@286 628 /**
ohair@286 629 * Creates a new instance of the specified class.
ohair@286 630 * In the unmarshaller, we need to check the user-specified factory class.
ohair@286 631 */
ohair@286 632 public Object createInstance( JaxBeanInfo beanInfo ) throws SAXException {
ohair@286 633 if(!factories.isEmpty()) {
ohair@286 634 Factory factory = factories.get(beanInfo.jaxbType);
ohair@286 635 if(factory!=null)
ohair@286 636 return factory.createInstance();
ohair@286 637 }
ohair@286 638 try {
ohair@286 639 return beanInfo.createInstance(this);
ohair@286 640 } catch (IllegalAccessException e) {
ohair@286 641 Loader.reportError("Unable to create an instance of "+beanInfo.jaxbType.getName(),e,false);
ohair@286 642 } catch (InvocationTargetException e) {
ohair@286 643 Loader.reportError("Unable to create an instance of "+beanInfo.jaxbType.getName(),e,false);
ohair@286 644 } catch (InstantiationException e) {
ohair@286 645 Loader.reportError("Unable to create an instance of "+beanInfo.jaxbType.getName(),e,false);
ohair@286 646 }
ohair@286 647 return null; // can never be here
ohair@286 648 }
ohair@286 649
ohair@286 650
ohair@286 651
ohair@286 652 //
ohair@286 653 //
ohair@286 654 // error handling
ohair@286 655 //
ohair@286 656 //
ohair@286 657
ohair@286 658 /**
ohair@286 659 * Reports an error to the user, and asks if s/he wants
ohair@286 660 * to recover. If the canRecover flag is false, regardless
ohair@286 661 * of the client instruction, an exception will be thrown.
ohair@286 662 *
ohair@286 663 * Only if the flag is true and the user wants to recover from an error,
ohair@286 664 * the method returns normally.
ohair@286 665 *
ohair@286 666 * The thrown exception will be catched by the unmarshaller.
ohair@286 667 */
ohair@286 668 public void handleEvent(ValidationEvent event, boolean canRecover ) throws SAXException {
ohair@286 669 ValidationEventHandler eventHandler = parent.getEventHandler();
ohair@286 670
ohair@286 671 boolean recover = eventHandler.handleEvent(event);
ohair@286 672
ohair@286 673 // if the handler says "abort", we will not return the object
ohair@286 674 // from the unmarshaller.getResult()
ohair@286 675 if(!recover) aborted = true;
ohair@286 676
ohair@286 677 if( !canRecover || !recover )
ohair@286 678 throw new SAXParseException2( event.getMessage(), locator,
ohair@286 679 new UnmarshalException(
ohair@286 680 event.getMessage(),
ohair@286 681 event.getLinkedException() ) );
ohair@286 682 }
ohair@286 683
mkos@397 684 @Override
ohair@286 685 public boolean handleEvent(ValidationEvent event) {
ohair@286 686 try {
ohair@286 687 // if the handler says "abort", we will not return the object.
ohair@286 688 boolean recover = parent.getEventHandler().handleEvent(event);
ohair@286 689 if(!recover) aborted = true;
ohair@286 690 return recover;
ohair@286 691 } catch( RuntimeException re ) {
ohair@286 692 // if client event handler causes a runtime exception, then we
ohair@286 693 // have to return false.
ohair@286 694 return false;
ohair@286 695 }
ohair@286 696 }
ohair@286 697
ohair@286 698 /**
ohair@286 699 * Reports an exception found during the unmarshalling to the user.
ohair@286 700 * This method is a convenience method that calls into
ohair@286 701 * {@link #handleEvent(ValidationEvent, boolean)}
ohair@286 702 */
ohair@286 703 public void handleError(Exception e) throws SAXException {
ohair@286 704 handleError(e,true);
ohair@286 705 }
ohair@286 706
ohair@286 707 public void handleError(Exception e,boolean canRecover) throws SAXException {
ohair@286 708 handleEvent(new ValidationEventImpl(ValidationEvent.ERROR,e.getMessage(),locator.getLocation(),e),canRecover);
ohair@286 709 }
ohair@286 710
ohair@286 711 public void handleError(String msg) {
ohair@286 712 handleEvent(new ValidationEventImpl(ValidationEvent.ERROR,msg,locator.getLocation()));
ohair@286 713 }
ohair@286 714
mkos@397 715 @Override
ohair@286 716 protected ValidationEventLocator getLocation() {
ohair@286 717 return locator.getLocation();
ohair@286 718 }
ohair@286 719
ohair@286 720 /**
ohair@286 721 * Gets the current source location information in SAX {@link Locator}.
ohair@286 722 * <p>
ohair@286 723 * Sometimes the unmarshaller works against a different kind of XML source,
ohair@286 724 * making this information meaningless.
ohair@286 725 */
ohair@286 726 public LocatorEx getLocator() { return locator; }
ohair@286 727
ohair@286 728 /**
ohair@286 729 * Called when there's no corresponding ID value.
ohair@286 730 */
ohair@286 731 public void errorUnresolvedIDREF(Object bean, String idref, LocatorEx loc) throws SAXException {
ohair@286 732 handleEvent( new ValidationEventImpl(
ohair@286 733 ValidationEvent.ERROR,
ohair@286 734 Messages.UNRESOLVED_IDREF.format(idref),
ohair@286 735 loc.getLocation()), true );
ohair@286 736 }
ohair@286 737
ohair@286 738
ohair@286 739 //
ohair@286 740 //
ohair@286 741 // ID/IDREF related code
ohair@286 742 //
ohair@286 743 //
ohair@286 744 /**
ohair@286 745 * Submitted patchers in the order they've submitted.
ohair@286 746 * Many XML vocabulary doesn't use ID/IDREF at all, so we
ohair@286 747 * initialize it with null.
ohair@286 748 */
ohair@286 749 private Patcher[] patchers = null;
ohair@286 750 private int patchersLen = 0;
ohair@286 751
ohair@286 752 /**
ohair@286 753 * Adds a job that will be executed at the last of the unmarshalling.
ohair@286 754 * This method is used to support ID/IDREF feature, but it can be used
ohair@286 755 * for other purposes as well.
ohair@286 756 *
ohair@286 757 * @param job
ohair@286 758 * The run method of this object is called.
ohair@286 759 */
ohair@286 760 public void addPatcher( Patcher job ) {
ohair@286 761 // re-allocate buffer if necessary
ohair@286 762 if( patchers==null )
ohair@286 763 patchers = new Patcher[32];
ohair@286 764 if( patchers.length == patchersLen ) {
ohair@286 765 Patcher[] buf = new Patcher[patchersLen*2];
ohair@286 766 System.arraycopy(patchers,0,buf,0,patchersLen);
ohair@286 767 patchers = buf;
ohair@286 768 }
ohair@286 769 patchers[patchersLen++] = job;
ohair@286 770 }
ohair@286 771
ohair@286 772 /** Executes all the patchers. */
ohair@286 773 private void runPatchers() throws SAXException {
ohair@286 774 if( patchers!=null ) {
ohair@286 775 for( int i=0; i<patchersLen; i++ ) {
ohair@286 776 patchers[i].run();
ohair@286 777 patchers[i] = null; // free memory
ohair@286 778 }
ohair@286 779 }
ohair@286 780 }
ohair@286 781
ohair@286 782 /**
ohair@286 783 * Adds the object which is currently being unmarshalled
ohair@286 784 * to the ID table.
ohair@286 785 *
ohair@286 786 * @return
ohair@286 787 * Returns the value passed as the parameter.
ohair@286 788 * This is a hack, but this makes it easier for ID
ohair@286 789 * transducer to do its job.
ohair@286 790 */
ohair@286 791 // TODO: what shall we do if the ID is already declared?
ohair@286 792 //
ohair@286 793 // throwing an exception is one way. Overwriting the previous one
ohair@286 794 // is another way. The latter allows us to process invalid documents,
ohair@286 795 // while the former makes it impossible to handle them.
ohair@286 796 //
ohair@286 797 // I prefer to be flexible in terms of invalid document handling,
ohair@286 798 // so chose not to throw an exception.
ohair@286 799 //
ohair@286 800 // I believe this is an implementation choice, not the spec issue.
ohair@286 801 // -kk
ohair@286 802 public String addToIdTable( String id ) throws SAXException {
ohair@286 803 // Hmm...
ohair@286 804 // in cases such as when ID is used as an attribute, or as @XmlValue
ohair@286 805 // the target wilil be current.target.
ohair@286 806 // but in some other cases, such as when ID is used as a child element
ohair@286 807 // or a value of JAXBElement, it's current.prev.target.
ohair@286 808 // I don't know if this detection logic is complete
ohair@286 809 Object o = current.target;
ohair@286 810 if(o==null)
ohair@286 811 o = current.prev.target;
ohair@286 812 idResolver.bind(id,o);
ohair@286 813 return id;
ohair@286 814 }
ohair@286 815
ohair@286 816 /**
ohair@286 817 * Looks up the ID table and gets associated object.
ohair@286 818 *
ohair@286 819 * <p>
ohair@286 820 * The exception thrown from {@link Callable#call()} means the unmarshaller should abort
ohair@286 821 * right away.
ohair@286 822 *
ohair@286 823 * @see IDResolver#resolve(String, Class)
ohair@286 824 */
ohair@286 825 public Callable getObjectFromId( String id, Class targetType ) throws SAXException {
ohair@286 826 return idResolver.resolve(id,targetType);
ohair@286 827 }
ohair@286 828
ohair@286 829 //
ohair@286 830 //
ohair@286 831 // namespace binding maintainance
ohair@286 832 //
ohair@286 833 //
ohair@286 834 private String[] nsBind = new String[16];
ohair@286 835 private int nsLen=0;
ohair@286 836
mkos@397 837 @Override
ohair@286 838 public void startPrefixMapping( String prefix, String uri ) {
ohair@286 839 if(nsBind.length==nsLen) {
ohair@286 840 // expand the buffer
ohair@286 841 String[] n = new String[nsLen*2];
ohair@286 842 System.arraycopy(nsBind,0,n,0,nsLen);
ohair@286 843 nsBind=n;
ohair@286 844 }
ohair@286 845 nsBind[nsLen++] = prefix;
ohair@286 846 nsBind[nsLen++] = uri;
ohair@286 847 }
mkos@397 848 @Override
ohair@286 849 public void endPrefixMapping( String prefix ) {
ohair@286 850 nsLen-=2;
ohair@286 851 }
ohair@286 852 private String resolveNamespacePrefix( String prefix ) {
ohair@286 853 if(prefix.equals("xml"))
ohair@286 854 return XMLConstants.XML_NS_URI;
ohair@286 855
ohair@286 856 for( int i=nsLen-2; i>=0; i-=2 ) {
ohair@286 857 if(prefix.equals(nsBind[i]))
ohair@286 858 return nsBind[i+1];
ohair@286 859 }
ohair@286 860
ohair@286 861 if(environmentNamespaceContext!=null)
ohair@286 862 // temporary workaround until Zephyr fixes 6337180
ohair@286 863 return environmentNamespaceContext.getNamespaceURI(prefix.intern());
ohair@286 864
ohair@286 865 // by default, the default ns is bound to "".
ohair@286 866 // but allow environmentNamespaceContext to take precedence
ohair@286 867 if(prefix.equals(""))
ohair@286 868 return "";
ohair@286 869
ohair@286 870 // unresolved. error.
ohair@286 871 return null;
ohair@286 872 }
ohair@286 873
ohair@286 874 /**
ohair@286 875 * Returns a list of prefixes newly declared on the current element.
ohair@286 876 *
ohair@286 877 * @return
ohair@286 878 * A possible zero-length array of prefixes. The default prefix
ohair@286 879 * is represented by the empty string.
ohair@286 880 */
ohair@286 881 public String[] getNewlyDeclaredPrefixes() {
ohair@286 882 return getPrefixList( current.prev.numNsDecl );
ohair@286 883 }
ohair@286 884
ohair@286 885 /**
ohair@286 886 * Returns a list of all in-scope prefixes.
ohair@286 887 *
ohair@286 888 * @return
ohair@286 889 * A possible zero-length array of prefixes. The default prefix
ohair@286 890 * is represented by the empty string.
ohair@286 891 */
ohair@286 892 public String[] getAllDeclaredPrefixes() {
ohair@286 893 return getPrefixList(0);
ohair@286 894 }
ohair@286 895
ohair@286 896 private String[] getPrefixList( int startIndex ) {
ohair@286 897 int size = (current.numNsDecl - startIndex)/2;
ohair@286 898 String[] r = new String[size];
ohair@286 899 for( int i=0; i<r.length; i++ )
ohair@286 900 r[i] = nsBind[startIndex+i*2];
ohair@286 901 return r;
ohair@286 902 }
ohair@286 903
ohair@286 904 // NamespaceContext2 implementation
ohair@286 905 //
mkos@397 906 @Override
ohair@286 907 public Iterator<String> getPrefixes(String uri) {
ohair@286 908 // TODO: could be implemented much faster
ohair@286 909 // wrap it into unmodifiable list so that the remove method
ohair@286 910 // will throw UnsupportedOperationException.
ohair@286 911 return Collections.unmodifiableList(
ohair@286 912 getAllPrefixesInList(uri)).iterator();
ohair@286 913 }
ohair@286 914
ohair@286 915 private List<String> getAllPrefixesInList(String uri) {
ohair@286 916 List<String> a = new ArrayList<String>();
ohair@286 917
ohair@286 918 if( uri==null )
ohair@286 919 throw new IllegalArgumentException();
ohair@286 920 if( uri.equals(XMLConstants.XML_NS_URI) ) {
ohair@286 921 a.add(XMLConstants.XML_NS_PREFIX);
ohair@286 922 return a;
ohair@286 923 }
ohair@286 924 if( uri.equals(XMLConstants.XMLNS_ATTRIBUTE_NS_URI) ) {
ohair@286 925 a.add(XMLConstants.XMLNS_ATTRIBUTE);
ohair@286 926 return a;
ohair@286 927 }
ohair@286 928
ohair@286 929 for( int i=nsLen-2; i>=0; i-=2 )
ohair@286 930 if(uri.equals(nsBind[i+1]))
ohair@286 931 if( getNamespaceURI(nsBind[i]).equals(nsBind[i+1]) )
ohair@286 932 // make sure that this prefix is still effective.
ohair@286 933 a.add(nsBind[i]);
ohair@286 934
ohair@286 935 return a;
ohair@286 936 }
ohair@286 937
mkos@397 938 @Override
ohair@286 939 public String getPrefix(String uri) {
ohair@286 940 if( uri==null )
ohair@286 941 throw new IllegalArgumentException();
ohair@286 942 if( uri.equals(XMLConstants.XML_NS_URI) )
ohair@286 943 return XMLConstants.XML_NS_PREFIX;
ohair@286 944 if( uri.equals(XMLConstants.XMLNS_ATTRIBUTE_NS_URI) )
ohair@286 945 return XMLConstants.XMLNS_ATTRIBUTE;
ohair@286 946
ohair@286 947 for( int i=nsLen-2; i>=0; i-=2 )
ohair@286 948 if(uri.equals(nsBind[i+1]))
ohair@286 949 if( getNamespaceURI(nsBind[i]).equals(nsBind[i+1]) )
ohair@286 950 // make sure that this prefix is still effective.
ohair@286 951 return nsBind[i];
ohair@286 952
ohair@286 953 if(environmentNamespaceContext!=null)
ohair@286 954 return environmentNamespaceContext.getPrefix(uri);
ohair@286 955
ohair@286 956 return null;
ohair@286 957 }
ohair@286 958
mkos@397 959 @Override
ohair@286 960 public String getNamespaceURI(String prefix) {
ohair@286 961 if (prefix == null)
ohair@286 962 throw new IllegalArgumentException();
ohair@286 963 if (prefix.equals(XMLConstants.XMLNS_ATTRIBUTE))
ohair@286 964 return XMLConstants.XMLNS_ATTRIBUTE_NS_URI;
ohair@286 965
ohair@286 966 return resolveNamespacePrefix(prefix);
ohair@286 967 }
ohair@286 968
ohair@286 969 //
ohair@286 970 //
ohair@286 971 //
ohair@286 972 // scope management
ohair@286 973 //
ohair@286 974 //
ohair@286 975 //
ohair@286 976 private Scope[] scopes = new Scope[16];
ohair@286 977 /**
ohair@286 978 * Points to the top of the scope stack (=size-1).
ohair@286 979 */
ohair@286 980 private int scopeTop=0;
ohair@286 981
ohair@286 982 {
ohair@286 983 for( int i=0; i<scopes.length; i++ )
ohair@286 984 scopes[i] = new Scope(this);
ohair@286 985 }
ohair@286 986
ohair@286 987 /**
ohair@286 988 * Starts a new packing scope.
ohair@286 989 *
ohair@286 990 * <p>
ohair@286 991 * This method allocates a specified number of fresh {@link Scope} objects.
ohair@286 992 * They can be accessed by the {@link #getScope} method until the corresponding
ohair@286 993 * {@link #endScope} method is invoked.
ohair@286 994 *
ohair@286 995 * <p>
ohair@286 996 * A new scope will mask the currently active scope. Only one frame of {@link Scope}s
ohair@286 997 * can be accessed at any given time.
ohair@286 998 *
ohair@286 999 * @param frameSize
ohair@286 1000 * The # of slots to be allocated.
ohair@286 1001 */
ohair@286 1002 public void startScope(int frameSize) {
ohair@286 1003 scopeTop += frameSize;
ohair@286 1004
ohair@286 1005 // reallocation
ohair@286 1006 if(scopeTop>=scopes.length) {
ohair@286 1007 Scope[] s = new Scope[Math.max(scopeTop+1,scopes.length*2)];
ohair@286 1008 System.arraycopy(scopes,0,s,0,scopes.length);
ohair@286 1009 for( int i=scopes.length; i<s.length; i++ )
ohair@286 1010 s[i] = new Scope(this);
ohair@286 1011 scopes = s;
ohair@286 1012 }
ohair@286 1013 }
ohair@286 1014
ohair@286 1015 /**
ohair@286 1016 * Ends the current packing scope.
ohair@286 1017 *
ohair@286 1018 * <p>
ohair@286 1019 * If any packing in progress will be finalized by this method.
ohair@286 1020 *
ohair@286 1021 * @param frameSize
ohair@286 1022 * The same size that gets passed to the {@link #startScope(int)}
ohair@286 1023 * method.
ohair@286 1024 */
ohair@286 1025 public void endScope(int frameSize) throws SAXException {
ohair@286 1026 try {
ohair@286 1027 for( ; frameSize>0; frameSize--, scopeTop-- )
ohair@286 1028 scopes[scopeTop].finish();
ohair@286 1029 } catch (AccessorException e) {
ohair@286 1030 handleError(e);
ohair@286 1031
ohair@286 1032 // the error might have left scopes in inconsistent state,
ohair@286 1033 // so replace them by fresh ones
ohair@286 1034 for( ; frameSize>0; frameSize-- )
ohair@286 1035 scopes[scopeTop--] = new Scope(this);
ohair@286 1036 }
ohair@286 1037 }
ohair@286 1038
ohair@286 1039 /**
ohair@286 1040 * Gets the currently active {@link Scope}.
ohair@286 1041 *
ohair@286 1042 * @param offset
ohair@286 1043 * a number between [0,frameSize)
ohair@286 1044 *
ohair@286 1045 * @return
ohair@286 1046 * always a valid {@link Scope} object.
ohair@286 1047 */
ohair@286 1048 public Scope getScope(int offset) {
ohair@286 1049 return scopes[scopeTop-offset];
ohair@286 1050 }
ohair@286 1051
ohair@286 1052 //
ohair@286 1053 //
ohair@286 1054 //
ohair@286 1055 //
ohair@286 1056 //
ohair@286 1057 //
ohair@286 1058 //
ohair@286 1059
ohair@286 1060 private static final Loader DEFAULT_ROOT_LOADER = new DefaultRootLoader();
ohair@286 1061 private static final Loader EXPECTED_TYPE_ROOT_LOADER = new ExpectedTypeRootLoader();
ohair@286 1062
ohair@286 1063 /**
ohair@286 1064 * Root loader that uses the tag name and possibly its @xsi:type
ohair@286 1065 * to decide how to start unmarshalling.
ohair@286 1066 */
ohair@286 1067 private static final class DefaultRootLoader extends Loader implements Receiver {
ohair@286 1068 /**
ohair@286 1069 * Receives the root element and determines how to start
ohair@286 1070 * unmarshalling.
ohair@286 1071 */
ohair@286 1072 @Override
ohair@286 1073 public void childElement(UnmarshallingContext.State state, TagName ea) throws SAXException {
ohair@286 1074 Loader loader = state.getContext().selectRootLoader(state,ea);
ohair@286 1075 if(loader!=null) {
ohair@286 1076 state.loader = loader;
ohair@286 1077 state.receiver = this;
ohair@286 1078 return;
ohair@286 1079 }
ohair@286 1080
ohair@286 1081 // the registry doesn't know about this element.
ohair@286 1082 // try its xsi:type
ohair@286 1083 JaxBeanInfo beanInfo = XsiTypeLoader.parseXsiType(state, ea, null);
ohair@286 1084 if(beanInfo==null) {
ohair@286 1085 // we don't even know its xsi:type
ohair@286 1086 reportUnexpectedChildElement(ea,false);
ohair@286 1087 return;
ohair@286 1088 }
ohair@286 1089
ohair@286 1090 state.loader = beanInfo.getLoader(null,false);
ohair@286 1091 state.prev.backup = new JAXBElement<Object>(ea.createQName(),Object.class,null);
ohair@286 1092 state.receiver = this;
ohair@286 1093 }
ohair@286 1094
ohair@286 1095 @Override
ohair@286 1096 public Collection<QName> getExpectedChildElements() {
ohair@286 1097 return getInstance().getJAXBContext().getValidRootNames();
ohair@286 1098 }
ohair@286 1099
mkos@397 1100 @Override
ohair@286 1101 public void receive(State state, Object o) {
ohair@286 1102 if(state.backup!=null) {
ohair@286 1103 ((JAXBElement<Object>)state.backup).setValue(o);
ohair@286 1104 o = state.backup;
ohair@286 1105 }
ohair@286 1106 if (state.nil) {
ohair@286 1107 ((JAXBElement<Object>)o).setNil(true);
ohair@286 1108 }
ohair@286 1109 state.getContext().result = o;
ohair@286 1110 }
ohair@286 1111 }
ohair@286 1112
ohair@286 1113 /**
ohair@286 1114 * Root loader that uses {@link UnmarshallingContext#expectedType}
ohair@286 1115 * to decide how to start unmarshalling.
ohair@286 1116 */
ohair@286 1117 private static final class ExpectedTypeRootLoader extends Loader implements Receiver {
ohair@286 1118 /**
ohair@286 1119 * Receives the root element and determines how to start
ohair@286 1120 * unmarshalling.
ohair@286 1121 */
ohair@286 1122 @Override
ohair@286 1123 public void childElement(UnmarshallingContext.State state, TagName ea) {
ohair@286 1124 UnmarshallingContext context = state.getContext();
ohair@286 1125
ohair@286 1126 // unmarshals the specified type
ohair@286 1127 QName qn = new QName(ea.uri,ea.local);
ohair@286 1128 state.prev.target = new JAXBElement(qn,context.expectedType.jaxbType,null,null);
ohair@286 1129 state.receiver = this;
ohair@286 1130 // this is bit wasteful, as in theory we should have each expectedType keep
ohair@286 1131 // nillable version --- but that increases the combination from two to four,
ohair@286 1132 // which adds the resident memory footprint. Since XsiNilLoader is small,
ohair@286 1133 // I intentionally allocate a new instance freshly.
ohair@286 1134 state.loader = new XsiNilLoader(context.expectedType.getLoader(null,true));
ohair@286 1135 }
ohair@286 1136
mkos@397 1137 @Override
ohair@286 1138 public void receive(State state, Object o) {
ohair@286 1139 JAXBElement e = (JAXBElement)state.target;
ohair@286 1140 e.setValue(o);
ohair@286 1141 state.getContext().recordOuterPeer(e);
ohair@286 1142 state.getContext().result = e;
ohair@286 1143 }
ohair@286 1144 }
ohair@286 1145
ohair@286 1146 //
ohair@286 1147 // in-place unmarshalling related capabilities
ohair@286 1148 //
ohair@286 1149 /**
ohair@286 1150 * Notifies the context about the inner peer of the current element.
ohair@286 1151 *
ohair@286 1152 * <p>
ohair@286 1153 * If the unmarshalling is building the association, the context
ohair@286 1154 * will use this information. Otherwise it will be just ignored.
ohair@286 1155 */
ohair@286 1156 public void recordInnerPeer(Object innerPeer) {
ohair@286 1157 if(assoc!=null)
ohair@286 1158 assoc.addInner(currentElement,innerPeer);
ohair@286 1159 }
ohair@286 1160
ohair@286 1161 /**
ohair@286 1162 * Gets the inner peer JAXB object associated with the current element.
ohair@286 1163 *
ohair@286 1164 * @return
ohair@286 1165 * null if the current element doesn't have an inner peer,
ohair@286 1166 * or if we are not doing the in-place unmarshalling.
ohair@286 1167 */
ohair@286 1168 public Object getInnerPeer() {
ohair@286 1169 if(assoc!=null && isInplaceMode)
ohair@286 1170 return assoc.getInnerPeer(currentElement);
ohair@286 1171 else
ohair@286 1172 return null;
ohair@286 1173 }
ohair@286 1174
ohair@286 1175 /**
ohair@286 1176 * Notifies the context about the outer peer of the current element.
ohair@286 1177 *
ohair@286 1178 * <p>
ohair@286 1179 * If the unmarshalling is building the association, the context
ohair@286 1180 * will use this information. Otherwise it will be just ignored.
ohair@286 1181 */
ohair@286 1182 public void recordOuterPeer(Object outerPeer) {
ohair@286 1183 if(assoc!=null)
ohair@286 1184 assoc.addOuter(currentElement,outerPeer);
ohair@286 1185 }
ohair@286 1186
ohair@286 1187 /**
ohair@286 1188 * Gets the outer peer JAXB object associated with the current element.
ohair@286 1189 *
ohair@286 1190 * @return
ohair@286 1191 * null if the current element doesn't have an inner peer,
ohair@286 1192 * or if we are not doing the in-place unmarshalling.
ohair@286 1193 */
ohair@286 1194 public Object getOuterPeer() {
ohair@286 1195 if(assoc!=null && isInplaceMode)
ohair@286 1196 return assoc.getOuterPeer(currentElement);
ohair@286 1197 else
ohair@286 1198 return null;
ohair@286 1199 }
ohair@286 1200
ohair@286 1201 /**
ohair@286 1202 * Gets the xmime:contentType value for the current object.
ohair@286 1203 *
ohair@286 1204 * @see JAXBContextImpl#getXMIMEContentType(Object)
ohair@286 1205 */
ohair@286 1206 public String getXMIMEContentType() {
ohair@286 1207 /*
ohair@286 1208 this won't work when the class is like
ohair@286 1209
ohair@286 1210 class Foo {
ohair@286 1211 @XmlValue Image img;
ohair@286 1212 }
ohair@286 1213
ohair@286 1214 because the target will return Foo, not the class enclosing Foo
ohair@286 1215 which will have xmime:contentType
ohair@286 1216 */
ohair@286 1217 Object t = current.target;
ohair@286 1218 if(t==null) return null;
ohair@286 1219 return getJAXBContext().getXMIMEContentType(t);
ohair@286 1220 }
ohair@286 1221
ohair@286 1222 /**
ohair@286 1223 * When called from within the realm of the unmarshaller, this method
ohair@286 1224 * returns the current {@link UnmarshallingContext} in charge.
ohair@286 1225 */
ohair@286 1226 public static UnmarshallingContext getInstance() {
ohair@286 1227 return (UnmarshallingContext) Coordinator._getInstance();
ohair@286 1228 }
ohair@286 1229
ohair@286 1230 /**
ohair@286 1231 * Allows to access elements which are expected in current state.
ohair@286 1232 * Useful for getting elements for current parent.
ohair@286 1233 *
ohair@286 1234 * @return
ohair@286 1235 */
ohair@286 1236 public Collection<QName> getCurrentExpectedElements() {
ohair@286 1237 pushCoordinator();
ohair@286 1238 try {
ohair@286 1239 State s = getCurrentState();
ohair@286 1240 Loader l = s.loader;
ohair@286 1241 return (l != null) ? l.getExpectedChildElements() : null;
ohair@286 1242 } finally {
ohair@286 1243 popCoordinator();
ohair@286 1244 }
ohair@286 1245 }
ohair@286 1246
ohair@286 1247 /**
ohair@286 1248 * Allows to access attributes which are expected in current state.
ohair@286 1249 * Useful for getting attributes for current parent.
ohair@286 1250 *
ohair@286 1251 * @return
ohair@286 1252 */
ohair@286 1253 public Collection<QName> getCurrentExpectedAttributes() {
ohair@286 1254 pushCoordinator();
ohair@286 1255 try {
ohair@286 1256 State s = getCurrentState();
ohair@286 1257 Loader l = s.loader;
ohair@286 1258 return (l != null) ? l.getExpectedAttributes() : null;
ohair@286 1259 } finally {
ohair@286 1260 popCoordinator();
ohair@286 1261 }
ohair@286 1262 }
ohair@286 1263
ohair@286 1264 /**
ohair@286 1265 * Gets StructureLoader if used as loader.
ohair@286 1266 * Useful when determining if element is mixed or not.
ohair@286 1267 *
ohair@286 1268 */
ohair@286 1269 public StructureLoader getStructureLoader() {
ohair@286 1270 if(current.loader instanceof StructureLoader)
ohair@286 1271 return (StructureLoader)current.loader;
ohair@286 1272
ohair@286 1273 return null;
ohair@286 1274 }
ohair@286 1275
mkos@397 1276 /**
mkos@397 1277 * Based on current {@link Logger} {@link Level} and errorCounter value determines if error should be reported.
mkos@397 1278 *
mkos@397 1279 * If the method called and return true it is expected that error will be reported. And that's why
mkos@397 1280 * errorCounter is automatically decremented during the check.
mkos@397 1281 *
mkos@397 1282 * NOT THREAD SAFE!!! In case of heave concurrency access several additional errors could be reported. It's not expected to be the
mkos@397 1283 * problem. Otherwise add synchronization here.
mkos@397 1284 *
mkos@397 1285 * @return true in case if {@link Level#FINEST} is set OR we haven't exceed errors reporting limit.
mkos@397 1286 */
mkos@397 1287 public boolean shouldErrorBeReported() throws SAXException {
mkos@397 1288 if (logger.isLoggable(Level.FINEST))
mkos@397 1289 return true;
mkos@397 1290
mkos@397 1291 if (errorsCounter >= 0) {
mkos@397 1292 --errorsCounter;
mkos@397 1293 if (errorsCounter == 0) // it's possible to miss this because of concurrency. If required add synchronization here
mkos@397 1294 handleEvent(new ValidationEventImpl(ValidationEvent.WARNING, Messages.ERRORS_LIMIT_EXCEEDED.format(),
mkos@397 1295 getLocator().getLocation(), null), true);
mkos@397 1296 }
mkos@397 1297 return errorsCounter >= 0;
mkos@397 1298 }
ohair@286 1299 }

mercurial