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