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 import java.util.Collection; 30 31 import javax.xml.namespace.QName; 32 import javax.xml.stream.XMLStreamException; 33 34 import com.sun.xml.internal.bind.api.AccessorException; 35 import com.sun.xml.internal.bind.v2.util.QNameMap; 36 import com.sun.xml.internal.bind.v2.model.runtime.RuntimePropertyInfo; 37 import com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl; 38 import com.sun.xml.internal.bind.v2.runtime.Name; 39 import com.sun.xml.internal.bind.v2.runtime.XMLSerializer; 40 import com.sun.xml.internal.bind.v2.runtime.reflect.Lister; 41 import com.sun.xml.internal.bind.v2.runtime.reflect.Accessor; 42 import com.sun.xml.internal.bind.v2.runtime.unmarshaller.ChildLoader; 43 import com.sun.xml.internal.bind.v2.runtime.unmarshaller.TagName; 44 import com.sun.xml.internal.bind.v2.runtime.unmarshaller.Loader; 45 import com.sun.xml.internal.bind.v2.runtime.unmarshaller.Receiver; 46 import com.sun.xml.internal.bind.v2.runtime.unmarshaller.Scope; 47 import com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext; 48 import com.sun.xml.internal.bind.v2.runtime.unmarshaller.XsiNilLoader; 49 50 import org.xml.sax.SAXException; 51 52 /** 53 * Commonality between {@link ArrayElementProperty} and {@link ArrayReferenceNodeProperty}. 54 * 55 * Mostly handles the unmarshalling of the wrapper element. 56 * 57 * @author Kohsuke Kawaguchi 58 */ 59 abstract class ArrayERProperty<BeanT,ListT,ItemT> extends ArrayProperty<BeanT,ListT,ItemT> { 60 61 /** 62 * Wrapper tag name if any, or null. 63 */ 64 protected final Name wrapperTagName; 65 66 /** 67 * True if the wrapper tag name is nillable. 68 * Always false if {@link #wrapperTagName}==null. 69 */ 70 protected final boolean isWrapperNillable; 71 72 protected ArrayERProperty(JAXBContextImpl grammar, RuntimePropertyInfo prop, QName tagName, boolean isWrapperNillable) { 73 super(grammar,prop); 74 if(tagName==null) 75 this.wrapperTagName = null; 76 else 77 this.wrapperTagName = grammar.nameBuilder.createElementName(tagName); 78 this.isWrapperNillable = isWrapperNillable; 79 } 80 81 /** 82 * Used to handle the collection wrapper element. 83 */ 84 private static final class ItemsLoader extends Loader { 85 86 private final Accessor acc; 87 private final Lister lister; 88 89 public ItemsLoader(Accessor acc, Lister lister, QNameMap<ChildLoader> children) { 90 super(false); 91 this.acc = acc; 92 this.lister = lister; 93 this.children = children; 94 } 95 96 @Override 97 public void startElement(UnmarshallingContext.State state, TagName ea) throws SAXException { 98 UnmarshallingContext context = state.getContext(); 99 context.startScope(1); 100 // inherit the target so that our children can access its target 101 state.target = state.prev.target; 102 103 // start it now, so that even if there's no children we can still return empty collection 104 context.getScope(0).start(acc,lister); 105 } 106 107 private final QNameMap<ChildLoader> children; 108 109 @Override 110 public void childElement(UnmarshallingContext.State state, TagName ea) throws SAXException { 111 ChildLoader child = children.get(ea.uri,ea.local); 112 if (child == null) { 113 child = children.get(CATCH_ALL); 114 } 115 if (child == null) { 116 super.childElement(state,ea); 117 return; 118 } 119 state.loader = child.loader; 120 state.receiver = child.receiver; 121 } 122 123 @Override 124 public void leaveElement(UnmarshallingContext.State state, TagName ea) throws SAXException { 125 state.getContext().endScope(1); 126 } 127 128 @Override 129 public Collection<QName> getExpectedChildElements() { 130 return children.keySet(); 131 } 132 } 133 134 public final void serializeBody(BeanT o, XMLSerializer w, Object outerPeer) throws SAXException, AccessorException, IOException, XMLStreamException { 135 ListT list = acc.get(o); 136 137 if(list!=null) { 138 if(wrapperTagName!=null) { 139 w.startElement(wrapperTagName,null); 140 w.endNamespaceDecls(list); 141 w.endAttributes(); 142 } 143 144 serializeListBody(o,w,list); 145 146 if(wrapperTagName!=null) 147 w.endElement(); 148 } else { 149 // list is null 150 if(isWrapperNillable) { 151 w.startElement(wrapperTagName,null); 152 w.writeXsiNilTrue(); 153 w.endElement(); 154 } // otherwise don't print the wrapper tag name 155 } 156 } 157 158 /** 159 * Serializes the items of the list. 160 * This method is invoked after the necessary wrapper tag is produced (if necessary.) 161 * 162 * @param list 163 * always non-null. 164 */ 165 protected abstract void serializeListBody(BeanT o, XMLSerializer w, ListT list) throws IOException, XMLStreamException, SAXException, AccessorException; 166 167 /** 168 * Creates the unmarshaler to unmarshal the body. 169 */ 170 protected abstract void createBodyUnmarshaller(UnmarshallerChain chain, QNameMap<ChildLoader> loaders); 171 172 173 public final void buildChildElementUnmarshallers(UnmarshallerChain chain, QNameMap<ChildLoader> loaders) { 174 if(wrapperTagName!=null) { 175 UnmarshallerChain c = new UnmarshallerChain(chain.context); 176 QNameMap<ChildLoader> m = new QNameMap<ChildLoader>(); 177 createBodyUnmarshaller(c,m); 178 Loader loader = new ItemsLoader(acc, lister, m); 179 if(isWrapperNillable || chain.context.allNillable) 180 loader = new XsiNilLoader(loader); 181 loaders.put(wrapperTagName,new ChildLoader(loader,null)); 182 } else { 183 createBodyUnmarshaller(chain,loaders); 184 } 185 } 186 187 /** 188 * {@link Receiver} that puts the child object into the {@link Scope} object. 189 */ 190 protected final class ReceiverImpl implements Receiver { 191 private final int offset; 192 193 protected ReceiverImpl(int offset) { 194 this.offset = offset; 195 } 196 197 public void receive(UnmarshallingContext.State state, Object o) throws SAXException { 198 state.getContext().getScope(offset).add(acc,lister,o); 199 } 200 }}