001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common Development
008: * and Distribution License("CDDL") (collectively, the "License"). You
009: * may not use this file except in compliance with the License. You can obtain
010: * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
011: * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
012: * language governing permissions and limitations under the License.
013: *
014: * When distributing the software, include this License Header Notice in each
015: * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
016: * Sun designates this particular file as subject to the "Classpath" exception
017: * as provided by Sun in the GPL Version 2 section of the License file that
018: * accompanied this code. If applicable, add the following below the License
019: * Header, with the fields enclosed by brackets [] replaced by your own
020: * identifying information: "Portions Copyrighted [year]
021: * [name of copyright owner]"
022: *
023: * Contributor(s):
024: *
025: * If you wish your version of this file to be governed by only the CDDL or
026: * only the GPL Version 2, indicate your decision by adding "[Contributor]
027: * elects to include this software in this distribution under the [CDDL or GPL
028: * Version 2] license." If you don't indicate a single choice of license, a
029: * recipient has the option to distribute your version of this file under
030: * either the CDDL, the GPL Version 2 or to extend the choice of license to
031: * its licensees as provided above. However, if you add GPL Version 2 code
032: * and therefore, elected the GPL Version 2 license, then the option applies
033: * only if the new code is made subject to such option by the copyright
034: * holder.
035: */
036:
037: package com.sun.xml.bind.v2.runtime.property;
038:
039: import java.io.IOException;
040: import java.lang.reflect.Type;
041: import java.util.HashMap;
042: import java.util.List;
043: import java.util.Map;
044:
045: import javax.xml.bind.JAXBException;
046: import javax.xml.stream.XMLStreamException;
047:
048: import com.sun.xml.bind.api.AccessorException;
049: import com.sun.xml.bind.v2.model.core.PropertyKind;
050: import com.sun.xml.bind.v2.model.core.TypeRef;
051: import com.sun.xml.bind.v2.model.runtime.RuntimeElementPropertyInfo;
052: import com.sun.xml.bind.v2.model.runtime.RuntimeTypeRef;
053: import com.sun.xml.bind.v2.runtime.JAXBContextImpl;
054: import com.sun.xml.bind.v2.runtime.JaxBeanInfo;
055: import com.sun.xml.bind.v2.runtime.Name;
056: import com.sun.xml.bind.v2.runtime.RuntimeUtil;
057: import com.sun.xml.bind.v2.runtime.Transducer;
058: import com.sun.xml.bind.v2.runtime.XMLSerializer;
059: import com.sun.xml.bind.v2.runtime.reflect.Accessor;
060: import com.sun.xml.bind.v2.runtime.reflect.ListIterator;
061: import com.sun.xml.bind.v2.runtime.reflect.Lister;
062: import com.sun.xml.bind.v2.runtime.reflect.NullSafeAccessor;
063: import com.sun.xml.bind.v2.runtime.unmarshaller.ChildLoader;
064: import com.sun.xml.bind.v2.runtime.unmarshaller.DefaultValueLoaderDecorator;
065: import com.sun.xml.bind.v2.runtime.unmarshaller.Loader;
066: import com.sun.xml.bind.v2.runtime.unmarshaller.Receiver;
067: import com.sun.xml.bind.v2.runtime.unmarshaller.TextLoader;
068: import com.sun.xml.bind.v2.runtime.unmarshaller.XsiNilLoader;
069: import com.sun.xml.bind.v2.util.QNameMap;
070:
071: import org.xml.sax.SAXException;
072:
073: /**
074: * {@link Property} implementation for multi-value property that maps to an element.
075: *
076: * @author Kohsuke Kawaguchi
077: */
078: abstract class ArrayElementProperty<BeanT, ListT, ItemT> extends
079: ArrayERProperty<BeanT, ListT, ItemT> {
080:
081: private final Map<Class, TagAndType> typeMap = new HashMap<Class, TagAndType>();
082: /**
083: * Set by the constructor and reset in the {@link #wrapUp()} method.
084: */
085: private Map<TypeRef<Type, Class>, JaxBeanInfo> refs = new HashMap<TypeRef<Type, Class>, JaxBeanInfo>();
086: /**
087: * Set by the constructor and reset in the {@link #wrapUp()} method.
088: */
089: protected RuntimeElementPropertyInfo prop;
090:
091: /**
092: * Tag name used when we see null in the collection. Can be null.
093: */
094: private final Name nillableTagName;
095:
096: protected ArrayElementProperty(JAXBContextImpl grammar,
097: RuntimeElementPropertyInfo prop) {
098: super (grammar, prop, prop.getXmlName(), prop
099: .isCollectionNillable());
100: this .prop = prop;
101:
102: List<? extends RuntimeTypeRef> types = prop.getTypes();
103:
104: Name n = null;
105:
106: for (RuntimeTypeRef typeRef : types) {
107: Class type = (Class) typeRef.getTarget().getType();
108: if (type.isPrimitive())
109: type = RuntimeUtil.primitiveToBox.get(type);
110:
111: JaxBeanInfo beanInfo = grammar.getOrCreate(typeRef
112: .getTarget());
113: TagAndType tt = new TagAndType(grammar.nameBuilder
114: .createElementName(typeRef.getTagName()), beanInfo);
115: typeMap.put(type, tt);
116: refs.put(typeRef, beanInfo);
117: if (typeRef.isNillable() && n == null)
118: n = tt.tagName;
119: }
120:
121: nillableTagName = n;
122: }
123:
124: @Override
125: public void wrapUp() {
126: super .wrapUp();
127: refs = null;
128: prop = null; // avoid keeping model objects live
129: }
130:
131: protected void serializeListBody(BeanT beanT, XMLSerializer w,
132: ListT list) throws IOException, XMLStreamException,
133: SAXException, AccessorException {
134: ListIterator<ItemT> itr = lister.iterator(list, w);
135:
136: boolean isIdref = itr instanceof Lister.IDREFSIterator; // UGLY
137:
138: while (itr.hasNext()) {
139: try {
140: ItemT item = itr.next();
141: if (item != null) {
142: Class itemType = item.getClass();
143: if (isIdref)
144: // This should be the only place where we need to be aware
145: // that the iterator is iterating IDREFS.
146: itemType = ((Lister.IDREFSIterator) itr).last()
147: .getClass();
148:
149: // normally, this returns non-null
150: TagAndType tt = typeMap.get(itemType);
151: while (tt == null && itemType != null) {
152: // otherwise we'll just have to try the slow way
153: itemType = itemType.getSuperclass();
154: tt = typeMap.get(itemType);
155: }
156:
157: if (tt == null) {
158: // item is not of the expected type.
159: // w.reportError(new ValidationEventImpl(ValidationEvent.ERROR,
160: // Messages.UNEXPECTED_JAVA_TYPE.format(
161: // item.getClass().getName(),
162: // getExpectedClassNameList()
163: // ),
164: // w.getCurrentLocation(fieldName)));
165: // continue;
166:
167: // see the similar code in SingleElementNodeProperty.
168: // for the purpose of simple type substitution, make it a non-error
169:
170: w.startElement(typeMap.values().iterator()
171: .next().tagName, null);
172: w.childAsXsiType(item, fieldName, w.grammar
173: .getBeanInfo(Object.class));
174: } else {
175: w.startElement(tt.tagName, null);
176: serializeItem(tt.beanInfo, item, w);
177: }
178:
179: w.endElement();
180: } else {
181: if (nillableTagName != null) {
182: w.startElement(nillableTagName, null);
183: w.writeXsiNilTrue();
184: w.endElement();
185: }
186: }
187: } catch (JAXBException e) {
188: w.reportError(fieldName, e);
189: // recover by ignoring this item
190: }
191: }
192: }
193:
194: /**
195: * Serializes one item of the property.
196: */
197: protected abstract void serializeItem(JaxBeanInfo expected,
198: ItemT item, XMLSerializer w) throws SAXException,
199: AccessorException, IOException, XMLStreamException;
200:
201: public void createBodyUnmarshaller(UnmarshallerChain chain,
202: QNameMap<ChildLoader> loaders) {
203:
204: // all items go to the same lister,
205: // so they should share the same offset.
206: int offset = chain.allocateOffset();
207: Receiver recv = new ReceiverImpl(offset);
208:
209: for (RuntimeTypeRef typeRef : prop.getTypes()) {
210:
211: Name tagName = chain.context.nameBuilder
212: .createElementName(typeRef.getTagName());
213: Loader item = createItemUnmarshaller(chain, typeRef);
214:
215: if (typeRef.isNillable() || chain.context.allNillable)
216: item = new XsiNilLoader.Array(item);
217: if (typeRef.getDefaultValue() != null)
218: item = new DefaultValueLoaderDecorator(item, typeRef
219: .getDefaultValue());
220:
221: loaders.put(tagName, new ChildLoader(item, recv));
222: }
223: }
224:
225: public final PropertyKind getKind() {
226: return PropertyKind.ELEMENT;
227: }
228:
229: /**
230: * Creates a loader handler that unmarshals the body of the item.
231: *
232: * <p>
233: * This will be sandwiched into <item> ... </item>.
234: *
235: * <p>
236: * When unmarshalling the body of item, the Pack of {@link Lister} is available
237: * as the handler state.
238: *
239: * @param chain
240: * @param typeRef
241: */
242: private Loader createItemUnmarshaller(UnmarshallerChain chain,
243: RuntimeTypeRef typeRef) {
244: if (PropertyFactory.isLeaf(typeRef.getSource())) {
245: final Transducer xducer = typeRef.getTransducer();
246: return new TextLoader(xducer);
247: } else {
248: return refs.get(typeRef).getLoader(chain.context, true);
249: }
250: }
251:
252: public Accessor getElementPropertyAccessor(String nsUri,
253: String localName) {
254: if (wrapperTagName != null) {
255: if (wrapperTagName.equals(nsUri, localName))
256: return acc;
257: } else {
258: for (TagAndType tt : typeMap.values()) {
259: if (tt.tagName.equals(nsUri, localName))
260: // when we can't distinguish null and empty list, JAX-WS doesn't want to see
261: // null (just like any user apps), but since we are providing a raw accessor,
262: // which just grabs the value from the field, we wrap it so that it won't return
263: // null.
264: return new NullSafeAccessor<BeanT, ListT, Object>(
265: acc, lister);
266: }
267: }
268: return null;
269: }
270: }
|