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