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 javax.xml.namespace.QName; 29 30 import com.sun.xml.internal.bind.DatatypeConverterImpl; 31 import com.sun.xml.internal.bind.v2.WellKnownNamespace; 32 import com.sun.xml.internal.bind.v2.runtime.JaxBeanInfo; 33 import com.sun.istack.internal.Nullable; 34 35 import java.util.Collection; 36 import java.util.Collections; 37 import java.util.HashSet; 38 import org.xml.sax.Attributes; 39 import org.xml.sax.SAXException; 40 41 /** 42 * Looks at @xsi:type and forwards to the right {@link Loader}. 43 * 44 * @author Kohsuke Kawaguchi 45 */ 46 public class XsiTypeLoader extends Loader { 47 48 /** 49 * Use this when no @xsi:type was found. 50 */ 51 private final JaxBeanInfo defaultBeanInfo; 52 XsiTypeLoader(JaxBeanInfo defaultBeanInfo)53 public XsiTypeLoader(JaxBeanInfo defaultBeanInfo) { 54 super(true); 55 this.defaultBeanInfo = defaultBeanInfo; 56 } 57 startElement(UnmarshallingContext.State state, TagName ea)58 public void startElement(UnmarshallingContext.State state, TagName ea) throws SAXException { 59 JaxBeanInfo beanInfo = parseXsiType(state,ea,defaultBeanInfo); 60 if(beanInfo==null) 61 beanInfo = defaultBeanInfo; 62 63 Loader loader = beanInfo.getLoader(null,false); 64 state.setLoader(loader); 65 loader.startElement(state,ea); 66 } 67 parseXsiType(UnmarshallingContext.State state, TagName ea, @Nullable JaxBeanInfo defaultBeanInfo)68 /*pacakge*/ static JaxBeanInfo parseXsiType(UnmarshallingContext.State state, TagName ea, @Nullable JaxBeanInfo defaultBeanInfo) throws SAXException { 69 UnmarshallingContext context = state.getContext(); 70 JaxBeanInfo beanInfo = null; 71 72 // look for @xsi:type 73 Attributes atts = ea.atts; 74 int idx = atts.getIndex(WellKnownNamespace.XML_SCHEMA_INSTANCE,"type"); 75 76 if(idx>=0) { 77 // we'll consume the value only when it's a recognized value, 78 // so don't consume it just yet. 79 String value = atts.getValue(idx); 80 81 QName type = DatatypeConverterImpl._parseQName(value,context); 82 if(type==null) { 83 reportError(Messages.NOT_A_QNAME.format(value),true); 84 } else { 85 if(defaultBeanInfo!=null && defaultBeanInfo.getTypeNames().contains(type)) 86 // if this xsi:type is something that the default type can already handle, 87 // let it do so. This is added as a work around to bug https://jax-ws.dev.java.net/issues/show_bug.cgi?id=195 88 // where a redundant xsi:type="xs:dateTime" causes JAXB to unmarshal XMLGregorianCalendar, 89 // where Date is expected. 90 // this is not a complete fix, as we still won't be able to handle simple type substitution in general, 91 // but none-the-less 92 return defaultBeanInfo; 93 94 beanInfo = context.getJAXBContext().getGlobalType(type); 95 if(beanInfo==null) { // let's report an error 96 if (context.parent.hasEventHandler() // is somebody listening? 97 && context.shouldErrorBeReported()) { // should we report error? 98 String nearest = context.getJAXBContext().getNearestTypeName(type); 99 if(nearest!=null) 100 reportError(Messages.UNRECOGNIZED_TYPE_NAME_MAYBE.format(type,nearest),true); 101 else 102 reportError(Messages.UNRECOGNIZED_TYPE_NAME.format(type),true); 103 } 104 } 105 // TODO: resurrect the following check 106 // else 107 // if(!target.isAssignableFrom(actual)) { 108 // reportError(context, 109 // Messages.UNSUBSTITUTABLE_TYPE.format(value,actual.getName(),target.getName()), 110 // true); 111 // actual = targetBeanInfo; // ditto 112 // } 113 } 114 } 115 return beanInfo; 116 } 117 118 static final QName XsiTypeQNAME = new QName(WellKnownNamespace.XML_SCHEMA_INSTANCE,"type"); 119 120 @Override getExpectedAttributes()121 public Collection<QName> getExpectedAttributes() { 122 final Collection<QName> expAttrs = new HashSet<QName>(); 123 expAttrs.addAll(super.getExpectedAttributes()); 124 expAttrs.add(XsiTypeQNAME); 125 return Collections.unmodifiableCollection(expAttrs); 126 } 127 } 128