1 /* |
1 /* |
2 * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. |
2 * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. |
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
4 * |
4 * |
5 * This code is free software; you can redistribute it and/or modify it |
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 |
6 * under the terms of the GNU General Public License version 2 only, as |
7 * published by the Free Software Foundation. Oracle designates this |
7 * published by the Free Software Foundation. Oracle designates this |
33 import java.util.HashMap; |
33 import java.util.HashMap; |
34 import java.util.Iterator; |
34 import java.util.Iterator; |
35 import java.util.List; |
35 import java.util.List; |
36 import java.util.Map; |
36 import java.util.Map; |
37 import java.util.concurrent.Callable; |
37 import java.util.concurrent.Callable; |
|
38 import java.util.logging.Level; |
|
39 import java.util.logging.Logger; |
38 |
40 |
39 import javax.xml.XMLConstants; |
41 import javax.xml.XMLConstants; |
40 import javax.xml.bind.JAXBElement; |
42 import javax.xml.bind.JAXBElement; |
41 import javax.xml.bind.UnmarshalException; |
43 import javax.xml.bind.UnmarshalException; |
42 import javax.xml.bind.Unmarshaller; |
44 import javax.xml.bind.Unmarshaller; |
49 |
51 |
50 import com.sun.istack.internal.NotNull; |
52 import com.sun.istack.internal.NotNull; |
51 import com.sun.istack.internal.Nullable; |
53 import com.sun.istack.internal.Nullable; |
52 import com.sun.istack.internal.SAXParseException2; |
54 import com.sun.istack.internal.SAXParseException2; |
53 import com.sun.xml.internal.bind.IDResolver; |
55 import com.sun.xml.internal.bind.IDResolver; |
|
56 import com.sun.xml.internal.bind.Util; |
54 import com.sun.xml.internal.bind.api.AccessorException; |
57 import com.sun.xml.internal.bind.api.AccessorException; |
55 import com.sun.xml.internal.bind.api.ClassResolver; |
58 import com.sun.xml.internal.bind.api.ClassResolver; |
56 import com.sun.xml.internal.bind.unmarshaller.InfosetScanner; |
59 import com.sun.xml.internal.bind.unmarshaller.InfosetScanner; |
57 import com.sun.xml.internal.bind.v2.ClassFactory; |
60 import com.sun.xml.internal.bind.v2.ClassFactory; |
58 import com.sun.xml.internal.bind.v2.runtime.AssociationMap; |
61 import com.sun.xml.internal.bind.v2.runtime.AssociationMap; |
59 import com.sun.xml.internal.bind.v2.runtime.Coordinator; |
62 import com.sun.xml.internal.bind.v2.runtime.Coordinator; |
60 import com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl; |
63 import com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl; |
61 import com.sun.xml.internal.bind.v2.runtime.JaxBeanInfo; |
64 import com.sun.xml.internal.bind.v2.runtime.JaxBeanInfo; |
|
65 import java.util.logging.Level; |
|
66 import java.util.logging.Logger; |
62 |
67 |
63 import org.xml.sax.ErrorHandler; |
68 import org.xml.sax.ErrorHandler; |
64 import org.xml.sax.SAXException; |
69 import org.xml.sax.SAXException; |
65 import org.xml.sax.helpers.LocatorImpl; |
70 import org.xml.sax.helpers.LocatorImpl; |
66 |
71 |
74 * @author Kohsuke Kawaguchi |
79 * @author Kohsuke Kawaguchi |
75 */ |
80 */ |
76 public final class UnmarshallingContext extends Coordinator |
81 public final class UnmarshallingContext extends Coordinator |
77 implements NamespaceContext, ValidationEventHandler, ErrorHandler, XmlVisitor, XmlVisitor.TextPredictor { |
82 implements NamespaceContext, ValidationEventHandler, ErrorHandler, XmlVisitor, XmlVisitor.TextPredictor { |
78 |
83 |
|
84 private static final Logger logger = Logger.getLogger(UnmarshallingContext.class.getName()); |
|
85 |
79 /** |
86 /** |
80 * Root state. |
87 * Root state. |
81 */ |
88 */ |
82 private final State root; |
89 private final State root; |
83 |
90 |
173 /** |
180 /** |
174 * User-supplied {@link ClassLoader} for converting name to {@link Class}. |
181 * User-supplied {@link ClassLoader} for converting name to {@link Class}. |
175 * For backward compatibility, when null, use thread context classloader. |
182 * For backward compatibility, when null, use thread context classloader. |
176 */ |
183 */ |
177 public @Nullable ClassLoader classLoader; |
184 public @Nullable ClassLoader classLoader; |
|
185 |
|
186 /** |
|
187 * The variable introduced to avoid reporting n^10 similar errors. |
|
188 * After error is reported counter is decremented. When it became 0 - errors should not be reported any more. |
|
189 * |
|
190 * volatile is required to ensure that concurrent threads will see changed value |
|
191 */ |
|
192 private static volatile int errorsCounter = 10; |
178 |
193 |
179 /** |
194 /** |
180 * State information for each element. |
195 * State information for each element. |
181 */ |
196 */ |
182 public final class State { |
197 public final class State { |
258 */ |
273 */ |
259 public UnmarshallingContext getContext() { |
274 public UnmarshallingContext getContext() { |
260 return UnmarshallingContext.this; |
275 return UnmarshallingContext.this; |
261 } |
276 } |
262 |
277 |
|
278 @SuppressWarnings("LeakingThisInConstructor") |
263 private State(State prev) { |
279 private State(State prev) { |
264 this.prev = prev; |
280 this.prev = prev; |
265 if(prev!=null) |
281 if (prev!=null) { |
266 prev.next = this; |
282 prev.next = this; |
|
283 } |
267 } |
284 } |
268 |
285 |
269 private void push() { |
286 private void push() { |
270 if(next==null) |
287 if (logger.isLoggable(Level.FINEST)) { |
|
288 logger.log(Level.FINEST, "State.push"); |
|
289 } |
|
290 if (next==null) { |
|
291 assert current == this; |
271 allocateMoreStates(); |
292 allocateMoreStates(); |
|
293 } |
|
294 nil = false; |
272 State n = next; |
295 State n = next; |
273 n.numNsDecl = nsLen; |
296 n.numNsDecl = nsLen; |
274 current = n; |
297 current = n; |
275 } |
298 } |
276 |
299 |
277 private void pop() { |
300 private void pop() { |
|
301 if (logger.isLoggable(Level.FINEST)) { |
|
302 logger.log(Level.FINEST, "State.pop"); |
|
303 } |
278 assert prev!=null; |
304 assert prev!=null; |
279 loader = null; |
305 loader = null; |
280 nil = false; |
306 nil = false; |
281 receiver = null; |
307 receiver = null; |
282 intercepter = null; |
308 intercepter = null; |
379 private void allocateMoreStates() { |
405 private void allocateMoreStates() { |
380 // this method should be used only when we run out of a state. |
406 // this method should be used only when we run out of a state. |
381 assert current.next==null; |
407 assert current.next==null; |
382 |
408 |
383 State s = current; |
409 State s = current; |
384 for( int i=0; i<8; i++ ) |
410 for (int i=0; i<8; i++) { |
385 s = new State(s); |
411 s = new State(s); |
|
412 } |
386 } |
413 } |
387 |
414 |
388 public void clearStates() { |
415 public void clearStates() { |
389 State last = current; |
416 State last = current; |
390 while (last.next != null) last = last.next; |
417 while (last.next != null) last = last.next; |
434 |
461 |
435 factories.put(type,new Factory(factory,m)); |
462 factories.put(type,new Factory(factory,m)); |
436 } |
463 } |
437 } |
464 } |
438 |
465 |
|
466 @Override |
439 public void startDocument(LocatorEx locator, NamespaceContext nsContext) throws SAXException { |
467 public void startDocument(LocatorEx locator, NamespaceContext nsContext) throws SAXException { |
440 if(locator!=null) |
468 if(locator!=null) |
441 this.locator = locator; |
469 this.locator = locator; |
442 this.environmentNamespaceContext = nsContext; |
470 this.environmentNamespaceContext = nsContext; |
443 // reset the object |
471 // reset the object |
447 patchersLen=0; |
475 patchersLen=0; |
448 aborted = false; |
476 aborted = false; |
449 isUnmarshalInProgress = true; |
477 isUnmarshalInProgress = true; |
450 nsLen=0; |
478 nsLen=0; |
451 |
479 |
452 setThreadAffinity(); |
|
453 |
|
454 if(expectedType!=null) |
480 if(expectedType!=null) |
455 root.loader = EXPECTED_TYPE_ROOT_LOADER; |
481 root.loader = EXPECTED_TYPE_ROOT_LOADER; |
456 else |
482 else |
457 root.loader = DEFAULT_ROOT_LOADER; |
483 root.loader = DEFAULT_ROOT_LOADER; |
458 |
484 |
459 idResolver.startDocument(this); |
485 idResolver.startDocument(this); |
460 } |
486 } |
461 |
487 |
|
488 @Override |
462 public void startElement(TagName tagName) throws SAXException { |
489 public void startElement(TagName tagName) throws SAXException { |
463 pushCoordinator(); |
490 pushCoordinator(); |
464 try { |
491 try { |
465 _startElement(tagName); |
492 _startElement(tagName); |
466 } finally { |
493 } finally { |
484 assert current.loader!=null; // the childElement should register this |
511 assert current.loader!=null; // the childElement should register this |
485 // and tell the new child that you are activated |
512 // and tell the new child that you are activated |
486 current.loader.startElement(current,tagName); |
513 current.loader.startElement(current,tagName); |
487 } |
514 } |
488 |
515 |
|
516 @Override |
489 public void text(CharSequence pcdata) throws SAXException { |
517 public void text(CharSequence pcdata) throws SAXException { |
490 State cur = current; |
518 State cur = current; |
491 pushCoordinator(); |
519 pushCoordinator(); |
492 try { |
520 try { |
493 if(cur.elementDefaultValue!=null) { |
521 if(cur.elementDefaultValue!=null) { |
535 locator = DUMMY_INSTANCE; |
565 locator = DUMMY_INSTANCE; |
536 environmentNamespaceContext = null; |
566 environmentNamespaceContext = null; |
537 |
567 |
538 // at the successful completion, scope must be all closed |
568 // at the successful completion, scope must be all closed |
539 assert root==current; |
569 assert root==current; |
540 |
|
541 resetThreadAffinity(); |
|
542 } |
570 } |
543 |
571 |
544 /** |
572 /** |
545 * You should be always calling this through {@link TextPredictor}. |
573 * You should be always calling this through {@link TextPredictor}. |
546 */ |
574 */ |
547 @Deprecated |
575 @Deprecated |
|
576 @Override |
548 public boolean expectText() { |
577 public boolean expectText() { |
549 return current.loader.expectText; |
578 return current.loader.expectText; |
550 } |
579 } |
551 |
580 |
552 /** |
581 /** |
553 * You should be always getting {@link TextPredictor} from {@link XmlVisitor}. |
582 * You should be always getting {@link TextPredictor} from {@link XmlVisitor}. |
554 */ |
583 */ |
555 @Deprecated |
584 @Deprecated |
|
585 @Override |
556 public TextPredictor getPredictor() { |
586 public TextPredictor getPredictor() { |
557 return this; |
587 return this; |
558 } |
588 } |
559 |
589 |
|
590 @Override |
560 public UnmarshallingContext getContext() { |
591 public UnmarshallingContext getContext() { |
561 return this; |
592 return this; |
562 } |
593 } |
563 |
594 |
564 /** |
595 /** |
648 new UnmarshalException( |
679 new UnmarshalException( |
649 event.getMessage(), |
680 event.getMessage(), |
650 event.getLinkedException() ) ); |
681 event.getLinkedException() ) ); |
651 } |
682 } |
652 |
683 |
|
684 @Override |
653 public boolean handleEvent(ValidationEvent event) { |
685 public boolean handleEvent(ValidationEvent event) { |
654 try { |
686 try { |
655 // if the handler says "abort", we will not return the object. |
687 // if the handler says "abort", we will not return the object. |
656 boolean recover = parent.getEventHandler().handleEvent(event); |
688 boolean recover = parent.getEventHandler().handleEvent(event); |
657 if(!recover) aborted = true; |
689 if(!recover) aborted = true; |
678 |
710 |
679 public void handleError(String msg) { |
711 public void handleError(String msg) { |
680 handleEvent(new ValidationEventImpl(ValidationEvent.ERROR,msg,locator.getLocation())); |
712 handleEvent(new ValidationEventImpl(ValidationEvent.ERROR,msg,locator.getLocation())); |
681 } |
713 } |
682 |
714 |
|
715 @Override |
683 protected ValidationEventLocator getLocation() { |
716 protected ValidationEventLocator getLocation() { |
684 return locator.getLocation(); |
717 return locator.getLocation(); |
685 } |
718 } |
686 |
719 |
687 /** |
720 /** |
799 // |
832 // |
800 // |
833 // |
801 private String[] nsBind = new String[16]; |
834 private String[] nsBind = new String[16]; |
802 private int nsLen=0; |
835 private int nsLen=0; |
803 |
836 |
|
837 @Override |
804 public void startPrefixMapping( String prefix, String uri ) { |
838 public void startPrefixMapping( String prefix, String uri ) { |
805 if(nsBind.length==nsLen) { |
839 if(nsBind.length==nsLen) { |
806 // expand the buffer |
840 // expand the buffer |
807 String[] n = new String[nsLen*2]; |
841 String[] n = new String[nsLen*2]; |
808 System.arraycopy(nsBind,0,n,0,nsLen); |
842 System.arraycopy(nsBind,0,n,0,nsLen); |
809 nsBind=n; |
843 nsBind=n; |
810 } |
844 } |
811 nsBind[nsLen++] = prefix; |
845 nsBind[nsLen++] = prefix; |
812 nsBind[nsLen++] = uri; |
846 nsBind[nsLen++] = uri; |
813 } |
847 } |
|
848 @Override |
814 public void endPrefixMapping( String prefix ) { |
849 public void endPrefixMapping( String prefix ) { |
815 nsLen-=2; |
850 nsLen-=2; |
816 } |
851 } |
817 private String resolveNamespacePrefix( String prefix ) { |
852 private String resolveNamespacePrefix( String prefix ) { |
818 if(prefix.equals("xml")) |
853 if(prefix.equals("xml")) |
866 return r; |
901 return r; |
867 } |
902 } |
868 |
903 |
869 // NamespaceContext2 implementation |
904 // NamespaceContext2 implementation |
870 // |
905 // |
|
906 @Override |
871 public Iterator<String> getPrefixes(String uri) { |
907 public Iterator<String> getPrefixes(String uri) { |
872 // TODO: could be implemented much faster |
908 // TODO: could be implemented much faster |
873 // wrap it into unmodifiable list so that the remove method |
909 // wrap it into unmodifiable list so that the remove method |
874 // will throw UnsupportedOperationException. |
910 // will throw UnsupportedOperationException. |
875 return Collections.unmodifiableList( |
911 return Collections.unmodifiableList( |
917 return environmentNamespaceContext.getPrefix(uri); |
954 return environmentNamespaceContext.getPrefix(uri); |
918 |
955 |
919 return null; |
956 return null; |
920 } |
957 } |
921 |
958 |
|
959 @Override |
922 public String getNamespaceURI(String prefix) { |
960 public String getNamespaceURI(String prefix) { |
923 if (prefix == null) |
961 if (prefix == null) |
924 throw new IllegalArgumentException(); |
962 throw new IllegalArgumentException(); |
925 if (prefix.equals(XMLConstants.XMLNS_ATTRIBUTE)) |
963 if (prefix.equals(XMLConstants.XMLNS_ATTRIBUTE)) |
926 return XMLConstants.XMLNS_ATTRIBUTE_NS_URI; |
964 return XMLConstants.XMLNS_ATTRIBUTE_NS_URI; |
1057 @Override |
1095 @Override |
1058 public Collection<QName> getExpectedChildElements() { |
1096 public Collection<QName> getExpectedChildElements() { |
1059 return getInstance().getJAXBContext().getValidRootNames(); |
1097 return getInstance().getJAXBContext().getValidRootNames(); |
1060 } |
1098 } |
1061 |
1099 |
|
1100 @Override |
1062 public void receive(State state, Object o) { |
1101 public void receive(State state, Object o) { |
1063 if(state.backup!=null) { |
1102 if(state.backup!=null) { |
1064 ((JAXBElement<Object>)state.backup).setValue(o); |
1103 ((JAXBElement<Object>)state.backup).setValue(o); |
1065 o = state.backup; |
1104 o = state.backup; |
1066 } |
1105 } |
1093 // which adds the resident memory footprint. Since XsiNilLoader is small, |
1132 // which adds the resident memory footprint. Since XsiNilLoader is small, |
1094 // I intentionally allocate a new instance freshly. |
1133 // I intentionally allocate a new instance freshly. |
1095 state.loader = new XsiNilLoader(context.expectedType.getLoader(null,true)); |
1134 state.loader = new XsiNilLoader(context.expectedType.getLoader(null,true)); |
1096 } |
1135 } |
1097 |
1136 |
|
1137 @Override |
1098 public void receive(State state, Object o) { |
1138 public void receive(State state, Object o) { |
1099 JAXBElement e = (JAXBElement)state.target; |
1139 JAXBElement e = (JAXBElement)state.target; |
1100 e.setValue(o); |
1140 e.setValue(o); |
1101 state.getContext().recordOuterPeer(e); |
1141 state.getContext().recordOuterPeer(e); |
1102 state.getContext().result = e; |
1142 state.getContext().result = e; |
1231 return (StructureLoader)current.loader; |
1271 return (StructureLoader)current.loader; |
1232 |
1272 |
1233 return null; |
1273 return null; |
1234 } |
1274 } |
1235 |
1275 |
|
1276 /** |
|
1277 * Based on current {@link Logger} {@link Level} and errorCounter value determines if error should be reported. |
|
1278 * |
|
1279 * If the method called and return true it is expected that error will be reported. And that's why |
|
1280 * errorCounter is automatically decremented during the check. |
|
1281 * |
|
1282 * NOT THREAD SAFE!!! In case of heave concurrency access several additional errors could be reported. It's not expected to be the |
|
1283 * problem. Otherwise add synchronization here. |
|
1284 * |
|
1285 * @return true in case if {@link Level#FINEST} is set OR we haven't exceed errors reporting limit. |
|
1286 */ |
|
1287 public boolean shouldErrorBeReported() throws SAXException { |
|
1288 if (logger.isLoggable(Level.FINEST)) |
|
1289 return true; |
|
1290 |
|
1291 if (errorsCounter >= 0) { |
|
1292 --errorsCounter; |
|
1293 if (errorsCounter == 0) // it's possible to miss this because of concurrency. If required add synchronization here |
|
1294 handleEvent(new ValidationEventImpl(ValidationEvent.WARNING, Messages.ERRORS_LIMIT_EXCEEDED.format(), |
|
1295 getLocator().getLocation(), null), true); |
|
1296 } |
|
1297 return errorsCounter >= 0; |
|
1298 } |
1236 } |
1299 } |