Fri, 04 Oct 2013 16:21:34 +0100
8025054: Update JAX-WS RI integration to 2.2.9-b130926.1035
Reviewed-by: chegar
ohair@286 | 1 | /* |
ohair@286 | 2 | * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. |
ohair@286 | 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
ohair@286 | 4 | * |
ohair@286 | 5 | * This code is free software; you can redistribute it and/or modify it |
ohair@286 | 6 | * under the terms of the GNU General Public License version 2 only, as |
ohair@286 | 7 | * published by the Free Software Foundation. Oracle designates this |
ohair@286 | 8 | * particular file as subject to the "Classpath" exception as provided |
ohair@286 | 9 | * by Oracle in the LICENSE file that accompanied this code. |
ohair@286 | 10 | * |
ohair@286 | 11 | * This code is distributed in the hope that it will be useful, but WITHOUT |
ohair@286 | 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
ohair@286 | 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
ohair@286 | 14 | * version 2 for more details (a copy is included in the LICENSE file that |
ohair@286 | 15 | * accompanied this code). |
ohair@286 | 16 | * |
ohair@286 | 17 | * You should have received a copy of the GNU General Public License version |
ohair@286 | 18 | * 2 along with this work; if not, write to the Free Software Foundation, |
ohair@286 | 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
ohair@286 | 20 | * |
ohair@286 | 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
ohair@286 | 22 | * or visit www.oracle.com if you need additional information or have any |
ohair@286 | 23 | * questions. |
ohair@286 | 24 | */ |
ohair@286 | 25 | |
ohair@286 | 26 | package com.sun.xml.internal.bind.v2.runtime.property; |
ohair@286 | 27 | |
ohair@286 | 28 | import java.io.IOException; |
ohair@286 | 29 | import java.util.HashMap; |
ohair@286 | 30 | import java.util.LinkedHashMap; |
ohair@286 | 31 | import java.util.Map; |
ohair@286 | 32 | import java.util.TreeMap; |
ohair@286 | 33 | import java.util.Collection; |
ohair@286 | 34 | import java.util.Collections; |
ohair@286 | 35 | import java.util.Arrays; |
ohair@286 | 36 | import java.util.Set; |
ohair@286 | 37 | |
ohair@286 | 38 | import javax.xml.stream.XMLStreamException; |
ohair@286 | 39 | import javax.xml.namespace.QName; |
ohair@286 | 40 | |
ohair@286 | 41 | import com.sun.xml.internal.bind.api.AccessorException; |
ohair@286 | 42 | import com.sun.xml.internal.bind.v2.ClassFactory; |
ohair@286 | 43 | import com.sun.xml.internal.bind.v2.util.QNameMap; |
ohair@286 | 44 | import com.sun.xml.internal.bind.v2.model.core.PropertyKind; |
ohair@286 | 45 | import com.sun.xml.internal.bind.v2.model.nav.ReflectionNavigator; |
ohair@286 | 46 | import com.sun.xml.internal.bind.v2.model.runtime.RuntimeMapPropertyInfo; |
ohair@286 | 47 | import com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl; |
ohair@286 | 48 | import com.sun.xml.internal.bind.v2.runtime.JaxBeanInfo; |
ohair@286 | 49 | import com.sun.xml.internal.bind.v2.runtime.Name; |
ohair@286 | 50 | import com.sun.xml.internal.bind.v2.runtime.XMLSerializer; |
ohair@286 | 51 | import com.sun.xml.internal.bind.v2.runtime.reflect.Accessor; |
ohair@286 | 52 | import com.sun.xml.internal.bind.v2.runtime.unmarshaller.ChildLoader; |
ohair@286 | 53 | import com.sun.xml.internal.bind.v2.runtime.unmarshaller.TagName; |
ohair@286 | 54 | import com.sun.xml.internal.bind.v2.runtime.unmarshaller.Loader; |
ohair@286 | 55 | import com.sun.xml.internal.bind.v2.runtime.unmarshaller.Receiver; |
ohair@286 | 56 | import com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext; |
ohair@286 | 57 | import com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext.State; |
ohair@286 | 58 | |
ohair@286 | 59 | import org.xml.sax.SAXException; |
ohair@286 | 60 | |
ohair@286 | 61 | /** |
ohair@286 | 62 | * @author Kohsuke Kawaguchi |
ohair@286 | 63 | */ |
ohair@286 | 64 | final class SingleMapNodeProperty<BeanT,ValueT extends Map> extends PropertyImpl<BeanT> { |
ohair@286 | 65 | |
ohair@286 | 66 | private final Accessor<BeanT,ValueT> acc; |
ohair@286 | 67 | /** |
ohair@286 | 68 | * The tag name that surrounds the whole property. |
ohair@286 | 69 | */ |
ohair@286 | 70 | private final Name tagName; |
ohair@286 | 71 | /** |
ohair@286 | 72 | * The tag name that corresponds to the 'entry' element. |
ohair@286 | 73 | */ |
ohair@286 | 74 | private final Name entryTag; |
ohair@286 | 75 | private final Name keyTag; |
ohair@286 | 76 | private final Name valueTag; |
ohair@286 | 77 | |
ohair@286 | 78 | private final boolean nillable; |
ohair@286 | 79 | |
ohair@286 | 80 | private JaxBeanInfo keyBeanInfo; |
ohair@286 | 81 | private JaxBeanInfo valueBeanInfo; |
ohair@286 | 82 | |
ohair@286 | 83 | /** |
ohair@286 | 84 | * The implementation class for this property. |
ohair@286 | 85 | * If the property is null, we create an instance of this class. |
ohair@286 | 86 | */ |
ohair@286 | 87 | private final Class<? extends ValueT> mapImplClass; |
ohair@286 | 88 | |
ohair@286 | 89 | public SingleMapNodeProperty(JAXBContextImpl context, RuntimeMapPropertyInfo prop) { |
ohair@286 | 90 | super(context, prop); |
ohair@286 | 91 | acc = prop.getAccessor().optimize(context); |
ohair@286 | 92 | this.tagName = context.nameBuilder.createElementName(prop.getXmlName()); |
ohair@286 | 93 | this.entryTag = context.nameBuilder.createElementName("","entry"); |
ohair@286 | 94 | this.keyTag = context.nameBuilder.createElementName("","key"); |
ohair@286 | 95 | this.valueTag = context.nameBuilder.createElementName("","value"); |
ohair@286 | 96 | this.nillable = prop.isCollectionNillable(); |
ohair@286 | 97 | this.keyBeanInfo = context.getOrCreate(prop.getKeyType()); |
ohair@286 | 98 | this.valueBeanInfo = context.getOrCreate(prop.getValueType()); |
ohair@286 | 99 | |
ohair@286 | 100 | // infer the implementation class |
ohair@286 | 101 | Class<ValueT> sig = ReflectionNavigator.REFLECTION.erasure(prop.getRawType()); |
ohair@286 | 102 | mapImplClass = ClassFactory.inferImplClass(sig,knownImplClasses); |
ohair@286 | 103 | // TODO: error check for mapImplClass==null |
ohair@286 | 104 | // what is the error reporting path for this part of the code? |
ohair@286 | 105 | } |
ohair@286 | 106 | |
ohair@286 | 107 | private static final Class[] knownImplClasses = { |
ohair@286 | 108 | HashMap.class, TreeMap.class, LinkedHashMap.class |
ohair@286 | 109 | }; |
ohair@286 | 110 | |
ohair@286 | 111 | public void reset(BeanT bean) throws AccessorException { |
ohair@286 | 112 | acc.set(bean,null); |
ohair@286 | 113 | } |
ohair@286 | 114 | |
ohair@286 | 115 | |
ohair@286 | 116 | /** |
ohair@286 | 117 | * A Map property can never be ID. |
ohair@286 | 118 | */ |
ohair@286 | 119 | public String getIdValue(BeanT bean) { |
ohair@286 | 120 | return null; |
ohair@286 | 121 | } |
ohair@286 | 122 | |
ohair@286 | 123 | public PropertyKind getKind() { |
ohair@286 | 124 | return PropertyKind.MAP; |
ohair@286 | 125 | } |
ohair@286 | 126 | |
ohair@286 | 127 | public void buildChildElementUnmarshallers(UnmarshallerChain chain, QNameMap<ChildLoader> handlers) { |
ohair@286 | 128 | keyLoader = keyBeanInfo.getLoader(chain.context,true); |
ohair@286 | 129 | valueLoader = valueBeanInfo.getLoader(chain.context,true); |
ohair@286 | 130 | handlers.put(tagName,new ChildLoader(itemsLoader,null)); |
ohair@286 | 131 | } |
ohair@286 | 132 | |
ohair@286 | 133 | private Loader keyLoader; |
ohair@286 | 134 | private Loader valueLoader; |
ohair@286 | 135 | |
ohair@286 | 136 | /** |
ohair@286 | 137 | * Handles <items> and </items>. |
ohair@286 | 138 | * |
ohair@286 | 139 | * The target will be set to a {@link Map}. |
ohair@286 | 140 | */ |
ohair@286 | 141 | private final Loader itemsLoader = new Loader(false) { |
ohair@286 | 142 | |
mkos@408 | 143 | private ThreadLocal<Stack<BeanT>> target = new ThreadLocal<Stack<BeanT>>(); |
mkos@408 | 144 | private ThreadLocal<Stack<ValueT>> map = new ThreadLocal<Stack<ValueT>>(); |
ohair@286 | 145 | |
ohair@286 | 146 | @Override |
ohair@286 | 147 | public void startElement(UnmarshallingContext.State state, TagName ea) throws SAXException { |
ohair@286 | 148 | // create or obtain the Map object |
ohair@286 | 149 | try { |
mkos@408 | 150 | BeanT target = (BeanT) state.prev.target; |
mkos@408 | 151 | ValueT mapValue = acc.get(target); |
mkos@408 | 152 | if(mapValue == null) |
mkos@408 | 153 | mapValue = ClassFactory.create(mapImplClass); |
mkos@408 | 154 | else |
mkos@408 | 155 | mapValue.clear(); |
mkos@408 | 156 | |
mkos@408 | 157 | Stack.push(this.target, target); |
mkos@408 | 158 | Stack.push(map, mapValue); |
mkos@408 | 159 | state.target = mapValue; |
ohair@286 | 160 | } catch (AccessorException e) { |
ohair@286 | 161 | // recover from error by setting a dummy Map that receives and discards the values |
ohair@286 | 162 | handleGenericException(e,true); |
ohair@286 | 163 | state.target = new HashMap(); |
ohair@286 | 164 | } |
ohair@286 | 165 | } |
ohair@286 | 166 | |
ohair@286 | 167 | @Override |
ohair@286 | 168 | public void leaveElement(State state, TagName ea) throws SAXException { |
ohair@286 | 169 | super.leaveElement(state, ea); |
ohair@286 | 170 | try { |
mkos@408 | 171 | acc.set(Stack.pop(target), Stack.pop(map)); |
ohair@286 | 172 | } catch (AccessorException ex) { |
ohair@286 | 173 | handleGenericException(ex,true); |
ohair@286 | 174 | } |
ohair@286 | 175 | } |
ohair@286 | 176 | |
ohair@286 | 177 | @Override |
ohair@286 | 178 | public void childElement(UnmarshallingContext.State state, TagName ea) throws SAXException { |
ohair@286 | 179 | if(ea.matches(entryTag)) { |
ohair@286 | 180 | state.loader = entryLoader; |
ohair@286 | 181 | } else { |
ohair@286 | 182 | super.childElement(state,ea); |
ohair@286 | 183 | } |
ohair@286 | 184 | } |
ohair@286 | 185 | |
ohair@286 | 186 | @Override |
ohair@286 | 187 | public Collection<QName> getExpectedChildElements() { |
ohair@286 | 188 | return Collections.singleton(entryTag.toQName()); |
ohair@286 | 189 | } |
ohair@286 | 190 | }; |
ohair@286 | 191 | |
ohair@286 | 192 | /** |
ohair@286 | 193 | * Handles <entry> and </entry>. |
ohair@286 | 194 | * |
ohair@286 | 195 | * The target will be set to a {@link Map}. |
ohair@286 | 196 | */ |
ohair@286 | 197 | private final Loader entryLoader = new Loader(false) { |
ohair@286 | 198 | @Override |
ohair@286 | 199 | public void startElement(UnmarshallingContext.State state, TagName ea) { |
ohair@286 | 200 | state.target = new Object[2]; // this is inefficient |
ohair@286 | 201 | } |
ohair@286 | 202 | |
ohair@286 | 203 | @Override |
ohair@286 | 204 | public void leaveElement(UnmarshallingContext.State state, TagName ea) { |
ohair@286 | 205 | Object[] keyValue = (Object[])state.target; |
ohair@286 | 206 | Map map = (Map) state.prev.target; |
ohair@286 | 207 | map.put(keyValue[0],keyValue[1]); |
ohair@286 | 208 | } |
ohair@286 | 209 | |
ohair@286 | 210 | @Override |
ohair@286 | 211 | public void childElement(UnmarshallingContext.State state, TagName ea) throws SAXException { |
ohair@286 | 212 | if(ea.matches(keyTag)) { |
ohair@286 | 213 | state.loader = keyLoader; |
ohair@286 | 214 | state.receiver = keyReceiver; |
ohair@286 | 215 | return; |
ohair@286 | 216 | } |
ohair@286 | 217 | if(ea.matches(valueTag)) { |
ohair@286 | 218 | state.loader = valueLoader; |
ohair@286 | 219 | state.receiver = valueReceiver; |
ohair@286 | 220 | return; |
ohair@286 | 221 | } |
ohair@286 | 222 | super.childElement(state,ea); |
ohair@286 | 223 | } |
ohair@286 | 224 | |
ohair@286 | 225 | @Override |
ohair@286 | 226 | public Collection<QName> getExpectedChildElements() { |
ohair@286 | 227 | return Arrays.asList(keyTag.toQName(),valueTag.toQName()); |
ohair@286 | 228 | } |
ohair@286 | 229 | }; |
ohair@286 | 230 | |
ohair@286 | 231 | private static final class ReceiverImpl implements Receiver { |
ohair@286 | 232 | private final int index; |
ohair@286 | 233 | public ReceiverImpl(int index) { |
ohair@286 | 234 | this.index = index; |
ohair@286 | 235 | } |
ohair@286 | 236 | public void receive(UnmarshallingContext.State state, Object o) { |
ohair@286 | 237 | ((Object[])state.target)[index] = o; |
ohair@286 | 238 | } |
ohair@286 | 239 | } |
ohair@286 | 240 | |
ohair@286 | 241 | private static final Receiver keyReceiver = new ReceiverImpl(0); |
ohair@286 | 242 | private static final Receiver valueReceiver = new ReceiverImpl(1); |
ohair@286 | 243 | |
ohair@286 | 244 | @Override |
ohair@286 | 245 | public void serializeBody(BeanT o, XMLSerializer w, Object outerPeer) throws SAXException, AccessorException, IOException, XMLStreamException { |
ohair@286 | 246 | ValueT v = acc.get(o); |
ohair@286 | 247 | if(v!=null) { |
ohair@286 | 248 | bareStartTag(w,tagName,v); |
ohair@286 | 249 | for( Map.Entry e : (Set<Map.Entry>)v.entrySet() ) { |
ohair@286 | 250 | bareStartTag(w,entryTag,null); |
ohair@286 | 251 | |
ohair@286 | 252 | Object key = e.getKey(); |
ohair@286 | 253 | if(key!=null) { |
ohair@286 | 254 | w.startElement(keyTag,key); |
ohair@286 | 255 | w.childAsXsiType(key,fieldName,keyBeanInfo, false); |
ohair@286 | 256 | w.endElement(); |
ohair@286 | 257 | } |
ohair@286 | 258 | |
ohair@286 | 259 | Object value = e.getValue(); |
ohair@286 | 260 | if(value!=null) { |
ohair@286 | 261 | w.startElement(valueTag,value); |
ohair@286 | 262 | w.childAsXsiType(value,fieldName,valueBeanInfo, false); |
ohair@286 | 263 | w.endElement(); |
ohair@286 | 264 | } |
ohair@286 | 265 | |
ohair@286 | 266 | w.endElement(); |
ohair@286 | 267 | } |
ohair@286 | 268 | w.endElement(); |
ohair@286 | 269 | } else |
ohair@286 | 270 | if(nillable) { |
ohair@286 | 271 | w.startElement(tagName,null); |
ohair@286 | 272 | w.writeXsiNilTrue(); |
ohair@286 | 273 | w.endElement(); |
ohair@286 | 274 | } |
ohair@286 | 275 | } |
ohair@286 | 276 | |
ohair@286 | 277 | private void bareStartTag(XMLSerializer w, Name tagName, Object peer) throws IOException, XMLStreamException, SAXException { |
ohair@286 | 278 | w.startElement(tagName,peer); |
ohair@286 | 279 | w.endNamespaceDecls(peer); |
ohair@286 | 280 | w.endAttributes(); |
ohair@286 | 281 | } |
ohair@286 | 282 | |
ohair@286 | 283 | @Override |
ohair@286 | 284 | public Accessor getElementPropertyAccessor(String nsUri, String localName) { |
ohair@286 | 285 | if(tagName.equals(nsUri,localName)) |
ohair@286 | 286 | return acc; |
ohair@286 | 287 | return null; |
ohair@286 | 288 | } |
mkos@408 | 289 | |
mkos@408 | 290 | private static final class Stack<T> { |
mkos@408 | 291 | private Stack<T> parent; |
mkos@408 | 292 | private T value; |
mkos@408 | 293 | |
mkos@408 | 294 | private Stack(Stack<T> parent, T value) { |
mkos@408 | 295 | this.parent = parent; |
mkos@408 | 296 | this.value = value; |
mkos@408 | 297 | } |
mkos@408 | 298 | |
mkos@408 | 299 | private Stack(T value) { |
mkos@408 | 300 | this.value = value; |
mkos@408 | 301 | } |
mkos@408 | 302 | |
mkos@408 | 303 | private static <T> void push(ThreadLocal<Stack<T>> holder, T value) { |
mkos@408 | 304 | Stack<T> parent = holder.get(); |
mkos@408 | 305 | if (parent == null) |
mkos@408 | 306 | holder.set(new Stack<T>(value)); |
mkos@408 | 307 | else |
mkos@408 | 308 | holder.set(new Stack<T>(parent, value)); |
mkos@408 | 309 | } |
mkos@408 | 310 | |
mkos@408 | 311 | private static <T> T pop(ThreadLocal<Stack<T>> holder) { |
mkos@408 | 312 | Stack<T> current = holder.get(); |
mkos@408 | 313 | if (current.parent == null) |
mkos@408 | 314 | holder.remove(); |
mkos@408 | 315 | else |
mkos@408 | 316 | holder.set(current.parent); |
mkos@408 | 317 | return current.value; |
mkos@408 | 318 | } |
mkos@408 | 319 | |
mkos@408 | 320 | } |
ohair@286 | 321 | } |