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

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

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

merge

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

mercurial