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.Map;
032:
033: import javax.xml.namespace.QName;
034: import javax.xml.stream.XMLStreamException;
035:
036: import com.sun.xml.internal.bind.api.AccessorException;
037: import com.sun.xml.internal.bind.v2.util.QNameMap;
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.RuntimeTypeInfo;
042: import com.sun.xml.internal.bind.v2.model.runtime.RuntimeTypeRef;
043: import com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl;
044: import com.sun.xml.internal.bind.v2.runtime.JaxBeanInfo;
045: import com.sun.xml.internal.bind.v2.runtime.Name;
046: import com.sun.xml.internal.bind.v2.runtime.XMLSerializer;
047: import com.sun.xml.internal.bind.v2.runtime.unmarshaller.Loader;
048: import com.sun.xml.internal.bind.v2.runtime.unmarshaller.ChildLoader;
049: import com.sun.xml.internal.bind.v2.runtime.unmarshaller.XsiNilLoader;
050: import com.sun.xml.internal.bind.v2.runtime.unmarshaller.DefaultValueLoaderDecorator;
051: import com.sun.xml.internal.bind.v2.runtime.unmarshaller.XsiTypeLoader;
052: import com.sun.xml.internal.bind.v2.runtime.reflect.Accessor;
053:
054: import org.xml.sax.SAXException;
055:
056: /**
057: * @author Kohsuke Kawaguchi (kk@kohsuke.org)
058: */
059: final class SingleElementNodeProperty<BeanT, ValueT> extends
060: PropertyImpl<BeanT> {
061:
062: private final Accessor<BeanT, ValueT> acc;
063:
064: private final boolean nillable;
065:
066: private final QName[] acceptedElements;
067:
068: private final Map<Class, TagAndType> typeNames = new HashMap<Class, TagAndType>();
069:
070: private RuntimeElementPropertyInfo prop;
071:
072: /**
073: * The tag name used to produce xsi:nil. The first one in the list.
074: */
075: private final Name nullTagName;
076:
077: public SingleElementNodeProperty(JAXBContextImpl context,
078: RuntimeElementPropertyInfo prop) {
079: super (context, prop);
080: acc = prop.getAccessor().optimize();
081: this .prop = prop;
082:
083: QName nt = null;
084: boolean nil = false;
085:
086: acceptedElements = new QName[prop.getTypes().size()];
087: for (int i = 0; i < acceptedElements.length; i++)
088: acceptedElements[i] = prop.getTypes().get(i).getTagName();
089:
090: for (RuntimeTypeRef e : prop.getTypes()) {
091: JaxBeanInfo beanInfo = context.getOrCreate(e.getTarget());
092: if (nt == null)
093: nt = e.getTagName();
094: typeNames.put(beanInfo.jaxbType, new TagAndType(
095: context.nameBuilder.createElementName(e
096: .getTagName()), beanInfo));
097: nil |= e.isNillable();
098: }
099:
100: nullTagName = context.nameBuilder.createElementName(nt);
101:
102: nillable = nil;
103: }
104:
105: public void wrapUp() {
106: super .wrapUp();
107: prop = null;
108: }
109:
110: public void reset(BeanT bean) throws AccessorException {
111: acc.set(bean, null);
112: }
113:
114: public String getIdValue(BeanT beanT) {
115: return null;
116: }
117:
118: public void serializeBody(BeanT o, XMLSerializer w, Object outerPeer)
119: throws SAXException, AccessorException, IOException,
120: XMLStreamException {
121: ValueT v = acc.get(o);
122: if (v != null) {
123: Class vtype = v.getClass();
124: TagAndType tt = typeNames.get(vtype); // quick way that usually works
125:
126: if (tt == null) {// slow way that always works
127: for (Map.Entry<Class, TagAndType> e : typeNames
128: .entrySet()) {
129: if (e.getKey().isAssignableFrom(vtype)) {
130: tt = e.getValue();
131: break;
132: }
133: }
134: }
135:
136: if (tt == null) {
137: // actually this is an error, because the actual type was not a sub-type
138: // of any of the types specified in the annotations,
139: // but for the purpose of experimenting with simple type substitution,
140: // it's convenient to marshal this anyway (for example so that classes
141: // generated from simple types like String can be marshalled as expected.)
142: w.startElement(
143: typeNames.values().iterator().next().tagName,
144: null);
145: w.childAsXsiType(v, fieldName, w.grammar
146: .getBeanInfo(Object.class));
147: } else {
148: w.startElement(tt.tagName, null);
149: w.childAsXsiType(v, fieldName, tt.beanInfo);
150: }
151: w.endElement();
152: } else if (nillable) {
153: w.startElement(nullTagName, null);
154: w.writeXsiNilTrue();
155: w.endElement();
156: }
157: }
158:
159: public void buildChildElementUnmarshallers(UnmarshallerChain chain,
160: QNameMap<ChildLoader> handlers) {
161: JAXBContextImpl context = chain.context;
162:
163: for (TypeRef<Type, Class> e : prop.getTypes()) {
164: JaxBeanInfo bi = context.getOrCreate((RuntimeTypeInfo) e
165: .getTarget());
166: Loader l = bi.getLoader(context, true);
167: if (e.getDefaultValue() != null)
168: l = new DefaultValueLoaderDecorator(l, e
169: .getDefaultValue());
170: if (nillable)
171: l = new XsiNilLoader.Single(l, acc);
172: handlers.put(e.getTagName(), new ChildLoader(l, acc));
173: }
174: }
175:
176: public PropertyKind getKind() {
177: return PropertyKind.ELEMENT;
178: }
179:
180: @Override
181: public Accessor getElementPropertyAccessor(String nsUri,
182: String localName) {
183: for (QName n : acceptedElements) {
184: if (n.getNamespaceURI().equals(nsUri)
185: && n.getLocalPart().equals(localName))
186: return acc;
187: }
188: return null;
189: }
190:
191: }
|