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