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

changeset 286
f50545b5e2f1
child 397
b99d7e355d4b
equal deleted inserted replaced
284:88b85470e72c 286:f50545b5e2f1
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 }

mercurial