001: /*
002: * Copyright 2006 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package com.sun.xml.internal.bind.v2.runtime.property;
027:
028: import java.io.IOException;
029: import java.lang.reflect.Type;
030: import java.util.HashMap;
031: import java.util.List;
032: import java.util.Map;
033:
034: import javax.xml.bind.JAXBException;
035: import javax.xml.stream.XMLStreamException;
036:
037: import com.sun.xml.internal.bind.api.AccessorException;
038: import com.sun.xml.internal.bind.v2.model.core.PropertyKind;
039: import com.sun.xml.internal.bind.v2.model.core.TypeRef;
040: import com.sun.xml.internal.bind.v2.model.runtime.RuntimeElementPropertyInfo;
041: import com.sun.xml.internal.bind.v2.model.runtime.RuntimeTypeRef;
042: import com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl;
043: import com.sun.xml.internal.bind.v2.runtime.JaxBeanInfo;
044: import com.sun.xml.internal.bind.v2.runtime.Name;
045: import com.sun.xml.internal.bind.v2.runtime.RuntimeUtil;
046: import com.sun.xml.internal.bind.v2.runtime.Transducer;
047: import com.sun.xml.internal.bind.v2.runtime.XMLSerializer;
048: import com.sun.xml.internal.bind.v2.runtime.reflect.Accessor;
049: import com.sun.xml.internal.bind.v2.runtime.reflect.ListIterator;
050: import com.sun.xml.internal.bind.v2.runtime.reflect.Lister;
051: import com.sun.xml.internal.bind.v2.runtime.reflect.NullSafeAccessor;
052: import com.sun.xml.internal.bind.v2.runtime.unmarshaller.ChildLoader;
053: import com.sun.xml.internal.bind.v2.runtime.unmarshaller.DefaultValueLoaderDecorator;
054: import com.sun.xml.internal.bind.v2.runtime.unmarshaller.Loader;
055: import com.sun.xml.internal.bind.v2.runtime.unmarshaller.Receiver;
056: import com.sun.xml.internal.bind.v2.runtime.unmarshaller.TextLoader;
057: import com.sun.xml.internal.bind.v2.runtime.unmarshaller.XsiNilLoader;
058: import com.sun.xml.internal.bind.v2.util.QNameMap;
059:
060: import org.xml.sax.SAXException;
061:
062: /**
063: * {@link Property} implementation for multi-value property that maps to an element.
064: *
065: * @author Kohsuke Kawaguchi
066: */
067: abstract class ArrayElementProperty<BeanT, ListT, ItemT> extends
068: ArrayERProperty<BeanT, ListT, ItemT> {
069:
070: private final Map<Class, TagAndType> typeMap = new HashMap<Class, TagAndType>();
071: /**
072: * Set by the constructor and reset in the {@link #wrapUp()} method.
073: */
074: private Map<TypeRef<Type, Class>, JaxBeanInfo> refs = new HashMap<TypeRef<Type, Class>, JaxBeanInfo>();
075: /**
076: * Set by the constructor and reset in the {@link #wrapUp()} method.
077: */
078: protected RuntimeElementPropertyInfo prop;
079:
080: /**
081: * Tag name used when we see null in the collection. Can be null.
082: */
083: private final Name nillableTagName;
084:
085: protected ArrayElementProperty(JAXBContextImpl grammar,
086: RuntimeElementPropertyInfo prop) {
087: super (grammar, prop, prop.getXmlName(), prop
088: .isCollectionNillable());
089: this .prop = prop;
090:
091: List<? extends RuntimeTypeRef> types = prop.getTypes();
092:
093: Name n = null;
094:
095: for (RuntimeTypeRef typeRef : types) {
096: Class type = (Class) typeRef.getTarget().getType();
097: if (type.isPrimitive())
098: type = RuntimeUtil.primitiveToBox.get(type);
099:
100: JaxBeanInfo beanInfo = grammar.getOrCreate(typeRef
101: .getTarget());
102: TagAndType tt = new TagAndType(grammar.nameBuilder
103: .createElementName(typeRef.getTagName()), beanInfo);
104: typeMap.put(type, tt);
105: refs.put(typeRef, beanInfo);
106: if (typeRef.isNillable() && n == null)
107: n = tt.tagName;
108: }
109:
110: nillableTagName = n;
111: }
112:
113: @Override
114: public void wrapUp() {
115: super .wrapUp();
116: refs = null;
117: prop = null; // avoid keeping model objects live
118: }
119:
120: protected void serializeListBody(BeanT beanT, XMLSerializer w,
121: ListT list) throws IOException, XMLStreamException,
122: SAXException, AccessorException {
123: ListIterator<ItemT> itr = lister.iterator(list, w);
124:
125: boolean isIdref = itr instanceof Lister.IDREFSIterator; // UGLY
126:
127: while (itr.hasNext()) {
128: try {
129: ItemT item = itr.next();
130: if (item != null) {
131: Class itemType = item.getClass();
132: if (isIdref)
133: // This should be the only place where we need to be aware
134: // that the iterator is iterating IDREFS.
135: itemType = ((Lister.IDREFSIterator) itr).last()
136: .getClass();
137:
138: // normally, this returns non-null
139: TagAndType tt = typeMap.get(itemType);
140: while (tt == null && itemType != null) {
141: // otherwise we'll just have to try the slow way
142: itemType = itemType.getSuperclass();
143: tt = typeMap.get(itemType);
144: }
145:
146: if (tt == null) {
147: // item is not of the expected type.
148: // w.reportError(new ValidationEventImpl(ValidationEvent.ERROR,
149: // Messages.UNEXPECTED_JAVA_TYPE.format(
150: // item.getClass().getName(),
151: // getExpectedClassNameList()
152: // ),
153: // w.getCurrentLocation(fieldName)));
154: // continue;
155:
156: // see the similar code in SingleElementNodeProperty.
157: // for the purpose of simple type substitution, make it a non-error
158:
159: w.startElement(typeMap.values().iterator()
160: .next().tagName, null);
161: w.childAsXsiType(item, fieldName, w.grammar
162: .getBeanInfo(Object.class));
163: } else {
164: w.startElement(tt.tagName, null);
165: serializeItem(tt.beanInfo, item, w);
166: }
167:
168: w.endElement();
169: } else {
170: if (nillableTagName != null) {
171: w.startElement(nillableTagName, null);
172: w.writeXsiNilTrue();
173: w.endElement();
174: }
175: }
176: } catch (JAXBException e) {
177: w.reportError(fieldName, e);
178: // recover by ignoring this item
179: }
180: }
181: }
182:
183: /**
184: * Serializes one item of the property.
185: */
186: protected abstract void serializeItem(JaxBeanInfo expected,
187: ItemT item, XMLSerializer w) throws SAXException,
188: AccessorException, IOException, XMLStreamException;
189:
190: public void createBodyUnmarshaller(UnmarshallerChain chain,
191: QNameMap<ChildLoader> loaders) {
192:
193: // all items go to the same lister,
194: // so they should share the same offset.
195: int offset = chain.allocateOffset();
196: Receiver recv = new ReceiverImpl(offset);
197:
198: for (RuntimeTypeRef typeRef : prop.getTypes()) {
199:
200: Name tagName = chain.context.nameBuilder
201: .createElementName(typeRef.getTagName());
202: Loader item = createItemUnmarshaller(chain, typeRef);
203:
204: if (typeRef.isNillable())
205: item = new XsiNilLoader.Array(item);
206: if (typeRef.getDefaultValue() != null)
207: item = new DefaultValueLoaderDecorator(item, typeRef
208: .getDefaultValue());
209:
210: loaders.put(tagName, new ChildLoader(item, recv));
211: }
212: }
213:
214: public final PropertyKind getKind() {
215: return PropertyKind.ELEMENT;
216: }
217:
218: /**
219: * Creates a loader handler that unmarshals the body of the item.
220: *
221: * <p>
222: * This will be sandwiched into <item> ... </item>.
223: *
224: * <p>
225: * When unmarshalling the body of item, the Pack of {@link Lister} is available
226: * as the handler state.
227: *
228: * @param chain
229: * @param typeRef
230: */
231: private Loader createItemUnmarshaller(UnmarshallerChain chain,
232: RuntimeTypeRef typeRef) {
233: if (PropertyFactory.isLeaf(typeRef.getSource())) {
234: final Transducer xducer = typeRef.getTransducer();
235: return new TextLoader(xducer);
236: } else {
237: return refs.get(typeRef).getLoader(chain.context, true);
238: }
239: }
240:
241: public Accessor getElementPropertyAccessor(String nsUri,
242: String localName) {
243: if (wrapperTagName != null) {
244: if (wrapperTagName.equals(nsUri, localName))
245: return acc;
246: } else {
247: for (TagAndType tt : typeMap.values()) {
248: if (tt.tagName.equals(nsUri, localName))
249: // when we can't distinguish null and empty list, JAX-WS doesn't want to see
250: // null (just like any user apps), but since we are providing a raw accessor,
251: // which just grabs the value from the field, we wrap it so that it won't return
252: // null.
253: return new NullSafeAccessor<BeanT, ListT, Object>(
254: acc, lister);
255: }
256: }
257: return null;
258: }
259: }
|