1 /*
   2  * Copyright (c) 1997, 2012, 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.property;
  27 
  28 import java.io.IOException;
  29 
  30 import javax.xml.bind.JAXBException;
  31 import javax.xml.bind.annotation.DomHandler;
  32 import javax.xml.stream.XMLStreamException;
  33 
  34 import com.sun.xml.internal.bind.v2.ClassFactory;
  35 import com.sun.xml.internal.bind.v2.model.core.PropertyKind;
  36 import com.sun.xml.internal.bind.v2.model.core.WildcardMode;
  37 import com.sun.xml.internal.bind.v2.model.runtime.RuntimeElement;
  38 import com.sun.xml.internal.bind.v2.model.runtime.RuntimeReferencePropertyInfo;
  39 import com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl;
  40 import com.sun.xml.internal.bind.v2.runtime.JaxBeanInfo;
  41 import com.sun.xml.internal.bind.v2.runtime.XMLSerializer;
  42 import com.sun.xml.internal.bind.v2.runtime.reflect.Accessor;
  43 import com.sun.xml.internal.bind.v2.runtime.reflect.ListIterator;
  44 import com.sun.xml.internal.bind.v2.runtime.unmarshaller.ChildLoader;
  45 import com.sun.xml.internal.bind.v2.runtime.unmarshaller.Loader;
  46 import com.sun.xml.internal.bind.v2.runtime.unmarshaller.Receiver;
  47 import com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext;
  48 import com.sun.xml.internal.bind.v2.runtime.unmarshaller.WildcardLoader;
  49 import com.sun.xml.internal.bind.v2.util.QNameMap;
  50 
  51 import org.xml.sax.SAXException;
  52 
  53 /**
  54  * @author Kohsuke Kawaguchi
  55  */
  56 class ArrayReferenceNodeProperty<BeanT,ListT,ItemT> extends ArrayERProperty<BeanT,ListT,ItemT> {
  57 
  58     /**
  59      * Expected element names and what class to unmarshal.
  60      */
  61     private final QNameMap<JaxBeanInfo> expectedElements = new QNameMap<JaxBeanInfo>();
  62 
  63     private final boolean isMixed;
  64 
  65     private final DomHandler domHandler;
  66     private final WildcardMode wcMode;
  67 
  68     public ArrayReferenceNodeProperty(JAXBContextImpl p, RuntimeReferencePropertyInfo prop) {
  69         super(p, prop, prop.getXmlName(), prop.isCollectionNillable());
  70 
  71         for (RuntimeElement e : prop.getElements()) {
  72             JaxBeanInfo bi = p.getOrCreate(e);
  73             expectedElements.put( e.getElementName().getNamespaceURI(),e.getElementName().getLocalPart(), bi );
  74         }
  75 
  76         isMixed = prop.isMixed();
  77 
  78         if(prop.getWildcard()!=null) {
  79             domHandler = (DomHandler) ClassFactory.create(prop.getDOMHandler());
  80             wcMode = prop.getWildcard();
  81         } else {
  82             domHandler = null;
  83             wcMode = null;
  84         }
  85     }
  86 
  87     protected final void serializeListBody(BeanT o, XMLSerializer w, ListT list) throws IOException, XMLStreamException, SAXException {
  88         ListIterator<ItemT> itr = lister.iterator(list, w);
  89 
  90         while(itr.hasNext()) {
  91             try {
  92                 ItemT item = itr.next();
  93                 if (item != null) {
  94                     if(isMixed && item.getClass()==String.class) {
  95                         w.text((String)item,null);
  96                     } else {
  97                         JaxBeanInfo bi = w.grammar.getBeanInfo(item,true);
  98                         if(bi.jaxbType==Object.class && domHandler!=null)
  99                             // even if 'v' is a DOM node, it always derive from Object,
 100                             // so the getBeanInfo returns BeanInfo for Object
 101                             w.writeDom(item,domHandler,o,fieldName);
 102                         else
 103                             bi.serializeRoot(item,w);
 104                     }
 105                 }
 106             } catch (JAXBException e) {
 107                 w.reportError(fieldName,e);
 108                 // recover by ignoring this item
 109             }
 110         }
 111     }
 112 
 113     public void createBodyUnmarshaller(UnmarshallerChain chain, QNameMap<ChildLoader> loaders) {
 114         final int offset = chain.allocateOffset();
 115 
 116         Receiver recv = new ReceiverImpl(offset);
 117 
 118         for( QNameMap.Entry<JaxBeanInfo> n : expectedElements.entrySet() ) {
 119             final JaxBeanInfo beanInfo = n.getValue();
 120             loaders.put(n.nsUri,n.localName,new ChildLoader(beanInfo.getLoader(chain.context,true),recv));
 121         }
 122 
 123         if(isMixed) {
 124             // handler for processing mixed contents.
 125             loaders.put(TEXT_HANDLER,
 126                 new ChildLoader(new MixedTextLoader(recv),null));
 127         }
 128 
 129         if(domHandler!=null) {
 130             loaders.put(CATCH_ALL,
 131                 new ChildLoader(new WildcardLoader(domHandler,wcMode),recv));
 132         }
 133     }
 134 
 135     private static final class MixedTextLoader extends Loader {
 136 
 137         private final Receiver recv;
 138 
 139         public MixedTextLoader(Receiver recv) {
 140             super(true);
 141             this.recv = recv;
 142         }
 143 
 144         public void text(UnmarshallingContext.State state, CharSequence text) throws SAXException {
 145             if(text.length()!=0) // length 0 text is pointless
 146                 recv.receive(state,text.toString());
 147         }
 148     }
 149 
 150 
 151     public PropertyKind getKind() {
 152         return PropertyKind.REFERENCE;
 153     }
 154 
 155     @Override
 156     public Accessor getElementPropertyAccessor(String nsUri, String localName) {
 157         // TODO: unwrap JAXBElement
 158         if(wrapperTagName!=null) {
 159             if(wrapperTagName.equals(nsUri,localName))
 160                 return acc;
 161         } else {
 162             if(expectedElements.containsKey(nsUri,localName))
 163                 return acc;
 164         }
 165         return null;
 166     }
 167 }