src/share/jaxws_classes/com/sun/xml/internal/bind/v2/runtime/property/SingleMapNodeProperty.java

Fri, 04 Oct 2013 16:21:34 +0100

author
mkos
date
Fri, 04 Oct 2013 16:21:34 +0100
changeset 408
b0610cd08440
parent 368
0989ad8c0860
child 450
b0c2840e2513
permissions
-rw-r--r--

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 &lt;items> and &lt;/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 &lt;entry> and &lt;/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 }

mercurial