Fri, 04 Oct 2013 16:21:34 +0100
8025054: Update JAX-WS RI integration to 2.2.9-b130926.1035
Reviewed-by: chegar
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 */
26 package com.sun.xml.internal.bind.v2.runtime.property;
28 import java.io.IOException;
29 import java.util.HashMap;
30 import java.util.LinkedHashMap;
31 import java.util.Map;
32 import java.util.TreeMap;
33 import java.util.Collection;
34 import java.util.Collections;
35 import java.util.Arrays;
36 import java.util.Set;
38 import javax.xml.stream.XMLStreamException;
39 import javax.xml.namespace.QName;
41 import com.sun.xml.internal.bind.api.AccessorException;
42 import com.sun.xml.internal.bind.v2.ClassFactory;
43 import com.sun.xml.internal.bind.v2.util.QNameMap;
44 import com.sun.xml.internal.bind.v2.model.core.PropertyKind;
45 import com.sun.xml.internal.bind.v2.model.nav.ReflectionNavigator;
46 import com.sun.xml.internal.bind.v2.model.runtime.RuntimeMapPropertyInfo;
47 import com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl;
48 import com.sun.xml.internal.bind.v2.runtime.JaxBeanInfo;
49 import com.sun.xml.internal.bind.v2.runtime.Name;
50 import com.sun.xml.internal.bind.v2.runtime.XMLSerializer;
51 import com.sun.xml.internal.bind.v2.runtime.reflect.Accessor;
52 import com.sun.xml.internal.bind.v2.runtime.unmarshaller.ChildLoader;
53 import com.sun.xml.internal.bind.v2.runtime.unmarshaller.TagName;
54 import com.sun.xml.internal.bind.v2.runtime.unmarshaller.Loader;
55 import com.sun.xml.internal.bind.v2.runtime.unmarshaller.Receiver;
56 import com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext;
57 import com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext.State;
59 import org.xml.sax.SAXException;
61 /**
62 * @author Kohsuke Kawaguchi
63 */
64 final class SingleMapNodeProperty<BeanT,ValueT extends Map> extends PropertyImpl<BeanT> {
66 private final Accessor<BeanT,ValueT> acc;
67 /**
68 * The tag name that surrounds the whole property.
69 */
70 private final Name tagName;
71 /**
72 * The tag name that corresponds to the 'entry' element.
73 */
74 private final Name entryTag;
75 private final Name keyTag;
76 private final Name valueTag;
78 private final boolean nillable;
80 private JaxBeanInfo keyBeanInfo;
81 private JaxBeanInfo valueBeanInfo;
83 /**
84 * The implementation class for this property.
85 * If the property is null, we create an instance of this class.
86 */
87 private final Class<? extends ValueT> mapImplClass;
89 public SingleMapNodeProperty(JAXBContextImpl context, RuntimeMapPropertyInfo prop) {
90 super(context, prop);
91 acc = prop.getAccessor().optimize(context);
92 this.tagName = context.nameBuilder.createElementName(prop.getXmlName());
93 this.entryTag = context.nameBuilder.createElementName("","entry");
94 this.keyTag = context.nameBuilder.createElementName("","key");
95 this.valueTag = context.nameBuilder.createElementName("","value");
96 this.nillable = prop.isCollectionNillable();
97 this.keyBeanInfo = context.getOrCreate(prop.getKeyType());
98 this.valueBeanInfo = context.getOrCreate(prop.getValueType());
100 // infer the implementation class
101 Class<ValueT> sig = ReflectionNavigator.REFLECTION.erasure(prop.getRawType());
102 mapImplClass = ClassFactory.inferImplClass(sig,knownImplClasses);
103 // TODO: error check for mapImplClass==null
104 // what is the error reporting path for this part of the code?
105 }
107 private static final Class[] knownImplClasses = {
108 HashMap.class, TreeMap.class, LinkedHashMap.class
109 };
111 public void reset(BeanT bean) throws AccessorException {
112 acc.set(bean,null);
113 }
116 /**
117 * A Map property can never be ID.
118 */
119 public String getIdValue(BeanT bean) {
120 return null;
121 }
123 public PropertyKind getKind() {
124 return PropertyKind.MAP;
125 }
127 public void buildChildElementUnmarshallers(UnmarshallerChain chain, QNameMap<ChildLoader> handlers) {
128 keyLoader = keyBeanInfo.getLoader(chain.context,true);
129 valueLoader = valueBeanInfo.getLoader(chain.context,true);
130 handlers.put(tagName,new ChildLoader(itemsLoader,null));
131 }
133 private Loader keyLoader;
134 private Loader valueLoader;
136 /**
137 * Handles <items> and </items>.
138 *
139 * The target will be set to a {@link Map}.
140 */
141 private final Loader itemsLoader = new Loader(false) {
143 private ThreadLocal<Stack<BeanT>> target = new ThreadLocal<Stack<BeanT>>();
144 private ThreadLocal<Stack<ValueT>> map = new ThreadLocal<Stack<ValueT>>();
146 @Override
147 public void startElement(UnmarshallingContext.State state, TagName ea) throws SAXException {
148 // create or obtain the Map object
149 try {
150 BeanT target = (BeanT) state.prev.target;
151 ValueT mapValue = acc.get(target);
152 if(mapValue == null)
153 mapValue = ClassFactory.create(mapImplClass);
154 else
155 mapValue.clear();
157 Stack.push(this.target, target);
158 Stack.push(map, mapValue);
159 state.target = mapValue;
160 } catch (AccessorException e) {
161 // recover from error by setting a dummy Map that receives and discards the values
162 handleGenericException(e,true);
163 state.target = new HashMap();
164 }
165 }
167 @Override
168 public void leaveElement(State state, TagName ea) throws SAXException {
169 super.leaveElement(state, ea);
170 try {
171 acc.set(Stack.pop(target), Stack.pop(map));
172 } catch (AccessorException ex) {
173 handleGenericException(ex,true);
174 }
175 }
177 @Override
178 public void childElement(UnmarshallingContext.State state, TagName ea) throws SAXException {
179 if(ea.matches(entryTag)) {
180 state.loader = entryLoader;
181 } else {
182 super.childElement(state,ea);
183 }
184 }
186 @Override
187 public Collection<QName> getExpectedChildElements() {
188 return Collections.singleton(entryTag.toQName());
189 }
190 };
192 /**
193 * Handles <entry> and </entry>.
194 *
195 * The target will be set to a {@link Map}.
196 */
197 private final Loader entryLoader = new Loader(false) {
198 @Override
199 public void startElement(UnmarshallingContext.State state, TagName ea) {
200 state.target = new Object[2]; // this is inefficient
201 }
203 @Override
204 public void leaveElement(UnmarshallingContext.State state, TagName ea) {
205 Object[] keyValue = (Object[])state.target;
206 Map map = (Map) state.prev.target;
207 map.put(keyValue[0],keyValue[1]);
208 }
210 @Override
211 public void childElement(UnmarshallingContext.State state, TagName ea) throws SAXException {
212 if(ea.matches(keyTag)) {
213 state.loader = keyLoader;
214 state.receiver = keyReceiver;
215 return;
216 }
217 if(ea.matches(valueTag)) {
218 state.loader = valueLoader;
219 state.receiver = valueReceiver;
220 return;
221 }
222 super.childElement(state,ea);
223 }
225 @Override
226 public Collection<QName> getExpectedChildElements() {
227 return Arrays.asList(keyTag.toQName(),valueTag.toQName());
228 }
229 };
231 private static final class ReceiverImpl implements Receiver {
232 private final int index;
233 public ReceiverImpl(int index) {
234 this.index = index;
235 }
236 public void receive(UnmarshallingContext.State state, Object o) {
237 ((Object[])state.target)[index] = o;
238 }
239 }
241 private static final Receiver keyReceiver = new ReceiverImpl(0);
242 private static final Receiver valueReceiver = new ReceiverImpl(1);
244 @Override
245 public void serializeBody(BeanT o, XMLSerializer w, Object outerPeer) throws SAXException, AccessorException, IOException, XMLStreamException {
246 ValueT v = acc.get(o);
247 if(v!=null) {
248 bareStartTag(w,tagName,v);
249 for( Map.Entry e : (Set<Map.Entry>)v.entrySet() ) {
250 bareStartTag(w,entryTag,null);
252 Object key = e.getKey();
253 if(key!=null) {
254 w.startElement(keyTag,key);
255 w.childAsXsiType(key,fieldName,keyBeanInfo, false);
256 w.endElement();
257 }
259 Object value = e.getValue();
260 if(value!=null) {
261 w.startElement(valueTag,value);
262 w.childAsXsiType(value,fieldName,valueBeanInfo, false);
263 w.endElement();
264 }
266 w.endElement();
267 }
268 w.endElement();
269 } else
270 if(nillable) {
271 w.startElement(tagName,null);
272 w.writeXsiNilTrue();
273 w.endElement();
274 }
275 }
277 private void bareStartTag(XMLSerializer w, Name tagName, Object peer) throws IOException, XMLStreamException, SAXException {
278 w.startElement(tagName,peer);
279 w.endNamespaceDecls(peer);
280 w.endAttributes();
281 }
283 @Override
284 public Accessor getElementPropertyAccessor(String nsUri, String localName) {
285 if(tagName.equals(nsUri,localName))
286 return acc;
287 return null;
288 }
290 private static final class Stack<T> {
291 private Stack<T> parent;
292 private T value;
294 private Stack(Stack<T> parent, T value) {
295 this.parent = parent;
296 this.value = value;
297 }
299 private Stack(T value) {
300 this.value = value;
301 }
303 private static <T> void push(ThreadLocal<Stack<T>> holder, T value) {
304 Stack<T> parent = holder.get();
305 if (parent == null)
306 holder.set(new Stack<T>(value));
307 else
308 holder.set(new Stack<T>(parent, value));
309 }
311 private static <T> T pop(ThreadLocal<Stack<T>> holder) {
312 Stack<T> current = holder.get();
313 if (current.parent == null)
314 holder.remove();
315 else
316 holder.set(current.parent);
317 return current.value;
318 }
320 }
321 }