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